From: Benoît Pin Date: Sun, 7 Nov 2010 02:08:56 +0000 (+0100) Subject: Importation initiale. X-Git-Url: https://scm.cri.ensmp.fr/git/PlinnDocument.git/commitdiff_plain/57a4d385a1d2806d5877f53b1fdb0bd94efa2dbb Importation initiale. --- 57a4d385a1d2806d5877f53b1fdb0bd94efa2dbb diff --git a/HistoryAdapters.py b/HistoryAdapters.py new file mode 100755 index 0000000..add0e8b --- /dev/null +++ b/HistoryAdapters.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +####################################################################################### +# Plinn - http://plinn.org # +# Copyright © 2005-2009 Benoît PIN # +# # +# This program is free software; you can redistribute it and/or # +# modify it under the terms of the GNU General Public License # +# as published by the Free Software Foundation; either version 2 # +# of the License, or (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program; if not, write to the Free Software # +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # +####################################################################################### +""" +Adapter to plug PlinnDocument to historycal interface. + +$Id: HistoryAdapters.py 647 2009-06-30 12:55:44Z pin $ +$URL: http://svn.cri.ensmp.fr/svn/PlinnDocument/branches/CMF-2.1/HistoryAdapters.py $ +""" + +from Globals import InitializeClass +from AccessControl import ClassSecurityInfo +from Products.CMFCore.permissions import ModifyPortalContent +from Products.Plinn.HistoryAdapters import DocumentHistory + +class PlinnDocumentHistory(DocumentHistory) : + + security = ClassSecurityInfo() + + security.declareProtected(ModifyPortalContent, 'restore') + def restore(self, key): + rev = self.getHistoricalRevisionByKey(key)[0] + self._content.edit(rev.Format(), rev.XMLBody()) + +InitializeClass(PlinnDocumentHistory) diff --git a/PlinnDocument.py b/PlinnDocument.py new file mode 100644 index 0000000..3267069 --- /dev/null +++ b/PlinnDocument.py @@ -0,0 +1,273 @@ +# -*- coding: utf-8 -*- +####################################################################################### +# Plinn - http://plinn.org # +# Copyright (C) 2005-2007 Benoît PIN # +# # +# This program is free software; you can redistribute it and/or # +# modify it under the terms of the GNU General Public License # +# as published by the Free Software Foundation; either version 2 # +# of the License, or (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program; if not, write to the Free Software # +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # +####################################################################################### + +from Globals import InitializeClass +from AccessControl import ClassSecurityInfo +from Products.CMFCore.permissions import View, ModifyPortalContent +from Products.CMFCore.utils import getToolByName +from Products.CMFDefault.Document import Document +from OFS.PropertyManager import PropertyManager +from OFS.Folder import Folder +from OFS.Image import File, cookId +from zope.component.factory import Factory +from zope.interface import implements +from Products.Photo import Photo +from Products.Plinn.utils import makeValidId +from interfaces import IPlinnDocument +from cStringIO import StringIO +from sets import Set +import xml.dom.minidom as minidom +import re + +imgPattern = re.compile('', re.IGNORECASE) +imgWidthPattern = re.compile('style\s*=\s*".*width\s*:\s*([0-9]+)px') +imgHeightPattern = re.compile('style\s*=\s*".*height\s*:\s*([0-9]+)px') +imgSrcPattern = re.compile('src\s*=\s*"(.*)"') + +imgOrLinkPattern = re.compile('(.*?))"(.*?)>|(.*?))"(.*?)>', re.IGNORECASE) +EMPTY_PLINN_DOCUMENT = '' + + +def addPlinnDocument(self, id, title='', description='', text=''): + """ Add a Plinn Document """ + o = PlinnDocument(id, title, description, text) + self._setObject(id,o) + +class PlinnDocument(Document) : + """ Plinn document - WYSIWYG editor + based on XML and javascript + """ + implements(IPlinnDocument) + + security = ClassSecurityInfo() + + _cookedTexts = {} + + def __init__(self, id, title='', description='', text='') : + self.attachments = Folder('attachments') + Document.__init__(self, id, title=title, description=description, text_format='html', text=text) + + security.declareProtected(View, 'EditableBody') + def EditableBody(self, mergeLayers=True): + """ Transforms XML to HTML """ + + if self.text : + if not self._cookedTexts.has_key(self.absolute_url()) : + plinnElement = minidom.parseString(self.text).documentElement + + htmlDom = minidom.parseString('
') + htmlDomDoc = htmlDom.documentElement + + self._transformRectangles(plinnElement, htmlDomDoc) + firstChildStyle = htmlDomDoc.firstChild.getAttribute('style') + htmlDomDoc.setAttribute('style', firstChildStyle.replace('absolute', 'relative')) + + if mergeLayers : + mergedDom = minidom.parseString('
') + mergedDomDoc = mergedDom.documentElement + for layer in htmlDomDoc.childNodes : + for foreignchild in layer.childNodes : + child = mergedDom.importNode(foreignchild, True) + mergedDomDoc.appendChild(child) + + mergedDomDoc.setAttribute('style', htmlDomDoc.getAttribute('style')) + htmlDom = mergedDom + + htmlText = htmlDom.toprettyxml().replace('<', '<').replace('>', '>').replace('"', '"').replace('&', '&') + htmlText = htmlText.encode('utf8') + htmlText = htmlText.split('\n', 1)[1] + + htmlText = imgOrLinkPattern.sub(self._convertSrcOrHref, htmlText) + self._cookedTexts[self.absolute_url()] = htmlText + return htmlText + else : + return self._cookedTexts[self.absolute_url()] + else : + return '' + + def _convertSrcOrHref(self, m) : + dict = m.groupdict() + if dict['src'] : + tag = m.group().replace(dict['src'], self._genAbsoluteUrl(dict['src'])) + if not tag.endswith('/>') : + tag = tag[:-1] + '/>' + return tag + elif dict['href'] : + return m.group().replace(dict['href'], self._genAbsoluteUrl(dict['href'])) + else: + return m.group() + + def _genAbsoluteUrl(self, relUrl) : + if relUrl.find('attachments/') >=0 : + return self.absolute_url() + '/' + relUrl[relUrl.rindex('attachments/'):] + else : + return relUrl + + + security.declareProtected(ModifyPortalContent, 'XMLBody') + def XMLBody(self, REQUEST=None) : + """ return raw xml text """ + + if REQUEST is not None : + RESPONSE = REQUEST['RESPONSE'] + RESPONSE.setHeader('content-type', 'text/xml; charset=utf-8') + + manager = getToolByName(self, 'caching_policy_manager', None) + if manager is not None: + view_name = 'XMLBody' + headers = manager.getHTTPCachingHeaders( + self, view_name, {} + ) + + for key, value in headers: + if key == 'ETag': + RESPONSE.setHeader(key, value, literal=1) + else: + RESPONSE.setHeader(key, value) + if headers: + RESPONSE.setHeader('X-Cache-Headers-Set-By', + 'CachingPolicyManager: %s' % + '/'.join(manager.getPhysicalPath())) + + + return Document.EditableBody(self) or EMPTY_PLINN_DOCUMENT + + + security.declareProtected(ModifyPortalContent, 'addAttachment') + def addAttachment(self, file, formId) : + """ Add attachment """ + id, title = cookId('', '', file) + + id = makeValidId(self.attachments, id) + + if formId == 'ImageUploadForm': + fileOb = Photo(id, title, file, thumb_height=300, thumb_width=300) + else : + fileOb = File(id, title, '') + fileOb.manage_upload(file) + + self.attachments._setObject(id, fileOb) + fileOb = getattr(self.attachments, id) + return fileOb + + + def _transformRectangles(self, inNode, outNode) : + + for node in [ node for node in inNode.childNodes if node.nodeName == 'rectangle' ] : + if node.getAttribute('visibility') == 'hidden' : + continue + + divRect = outNode.ownerDocument.createElement('div') + outNode.appendChild(divRect) + + styleAttr = 'position:absolute' + styleAttr += ';width:%spx' % node.getAttribute('width') + styleAttr += ';height:%spx' % node.getAttribute('height') + + for subNode in node.childNodes : + if subNode.nodeName == 'upperLeftCorner' : + for point in subNode.childNodes : + if point.nodeName == 'point' : + styleAttr += ';left:%spx' % point.getAttribute('x') + styleAttr += ';top:%spx' % point.getAttribute('y') + divRect.setAttribute('style', styleAttr) + break + + elif subNode.nodeName == 'rawData' : + rawData = subNode.firstChild + if rawData : + textNode = outNode.ownerDocument.createTextNode(self.getElementTransform(node.getAttribute('elementKey'))(node, rawData.nodeValue)) + divRect.appendChild(textNode) + + self._transformRectangles(node, divRect) + + + security.declarePrivate('renderImg') + def renderImg(self, node, raw) : + width = int(node.getAttribute('width')) + height = int(node.getAttribute('height')) + + photoId = raw.split('/')[-2] + photo = self._resizePhoto(photoId, width, height) + + alt = 'image' + return '%(alt)s' % \ + {'src' : photo.absolute_url(), 'width' : width, 'height' : height, 'alt' : alt} + + + security.declarePrivate('renderEpozImg') + def renderEpozImg(self, node, raw): + for img in imgPattern.findall(raw) : + width = imgWidthPattern.findall(img) + if width : width = int(width[0]) + + height = imgHeightPattern.findall(img) + if height : height = int(height[0]) + + if not (width or height) : continue # default size + + photoId = imgSrcPattern.findall(img)[0].split('/')[-2] + self._resizePhoto(photoId, width, height) + + return raw + + + def _resizePhoto(self, photoId, width, height): + photo = getattr(self.attachments, photoId) + + nts = [width, height] + landscape = width > height + if landscape : + nts.reverse() + + thumbSize = {'width': photo.thumb_width, 'height': photo.thumb_height} + + if thumbSize['width'] != nts[0] or thumbSize['height'] != nts[1] > 1 : + photo.manage_editProperties(thumb_width=nts[0], thumb_height=nts[1]) + + return photo + + + security.declarePrivate('getElementTransform') + def getElementTransform(self, elementKey) : + transforms = {'IMG_ELEMENT': self.renderImg, + 'EPOZ_ELEMENT': self.renderEpozImg} + return transforms.get(elementKey, lambda node, raw : raw) + + def _edit(self, text): + """ Edit the Document and cook the body. + """ + Document._edit(self, text) + self._removeUnusedAttachments() + self._cookedTexts = {} + + + def _removeUnusedAttachments(self) : + if not(self.attachments.objectIds() and self.XMLBody()) : return + + reAttachments = re.compile('|'.join( [r'\b%s\b' % id for id in self.attachments.objectIds()] )) + xml = self.XMLBody() + attSet = Set(self.attachments.objectIds()) + useSet = Set(reAttachments.findall(xml)) + self.attachments.manage_delObjects([att for att in attSet - useSet]) + + +InitializeClass(PlinnDocument) +PlinnDocumentFactory = Factory(PlinnDocument) \ No newline at end of file diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..a2dc2a1 --- /dev/null +++ b/__init__.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +####################################################################################### +# Plinn - http://plinn.org # +# Copyright (C) 2005-2007 Benoît PIN # +# # +# This program is free software; you can redistribute it and/or # +# modify it under the terms of the GNU General Public License # +# as published by the Free Software Foundation; either version 2 # +# of the License, or (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program; if not, write to the Free Software # +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # +####################################################################################### + +from Products.CMFCore import utils +from Products.CMFCore.permissions import AddPortalContent + +import PlinnDocument + +contentClasses = (PlinnDocument.PlinnDocument,) +contentConstructors = (PlinnDocument.addPlinnDocument,) + +def initialize(registrar) : + utils.ContentInit( + 'Plinn Content', + content_types = contentClasses, + permission = AddPortalContent, + extra_constructors = contentConstructors, + ).initialize(registrar) \ No newline at end of file diff --git a/configure.zcml b/configure.zcml new file mode 100644 index 0000000..e72308a --- /dev/null +++ b/configure.zcml @@ -0,0 +1,32 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/graphics/delete_rectangle.psd b/graphics/delete_rectangle.psd new file mode 100644 index 0000000..8a6823c Binary files /dev/null and b/graphics/delete_rectangle.psd differ diff --git a/graphics/layer_status.psd b/graphics/layer_status.psd new file mode 100644 index 0000000..5725cb7 Binary files /dev/null and b/graphics/layer_status.psd differ diff --git a/graphics/resize_handle.psd b/graphics/resize_handle.psd new file mode 100644 index 0000000..2baf5c0 Binary files /dev/null and b/graphics/resize_handle.psd differ diff --git a/interfaces.py b/interfaces.py new file mode 100755 index 0000000..ca0edbd --- /dev/null +++ b/interfaces.py @@ -0,0 +1,5 @@ +from zope.interface import Interface + +class IPlinnDocument(Interface): + """ Plinn document + """ diff --git a/license.txt b/license.txt new file mode 100755 index 0000000..3912109 --- /dev/null +++ b/license.txt @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/profiles/default/types.xml b/profiles/default/types.xml new file mode 100644 index 0000000..04e5f83 --- /dev/null +++ b/profiles/default/types.xml @@ -0,0 +1,4 @@ + + + + diff --git a/profiles/default/types/Plinn_Document.xml b/profiles/default/types/Plinn_Document.xml new file mode 100644 index 0000000..bce5392 --- /dev/null +++ b/profiles/default/types/Plinn_Document.xml @@ -0,0 +1,42 @@ + + + Plinn Document + Plinn Documents contain text and images that can be layout with WYSIWYG and drag&drop editor. + plinn_doc.gif + Plinn Document + + plinndocument + object/edit + True + True + + False + + + + + + + + + + + + + + + + + + diff --git a/skins/color_utils.js b/skins/color_utils.js new file mode 100644 index 0000000..7c70d07 --- /dev/null +++ b/skins/color_utils.js @@ -0,0 +1,117 @@ +// (c) Benoît PIN 2006-2007 +// http://plinn.org +// Licence GPL + +function HSVColor(h, s, v) { + this.h = h; // 0-360° + this.s = s; // 0-100% + this.v = v; // 0-100% +} + +HSVColor.prototype.toRGBString = function() { + var rgb = this.toRGB(); + return "rgb(" + String(rgb[0]) + ", " + String(rgb[1]) + ", " + String(rgb[2]) + ")"; +}; + +HSVColor.prototype.toRGB = function () { + var h = this.h; var s = this.s/100.0; + var v = this.v/100.0; + if( s == 0 ) // achromatic (grey) + r = g = b = v; + + h = h/60; // sector 0 to 5 + var i = Math.floor( h ); + var f = h - i; // factorial part of h + var p = v * ( 1 - s ); + var q = v * ( 1 - s * f ); + var t = v * ( 1 - s * ( 1 - f ) ); + switch( i ) { + case 0: + r = v; + g = t; + b = p; + break; + case 1: + r = q; + g = v; + b = p; + break; + case 2: + r = p; + g = v; + b = t; + break; + case 3: + r = p; + g = q; + b = v; + break; + case 4: + r = t; + g = p; + b = v; + break; + default: // case 5: + r = v; + g = p; + b = q; + break; + } + + r = Math.round(r*255); + g = Math.round(g*255); + b = Math.round(b*255); + return [r, g, b]; +}; + + +function ColorIterator(baseH, baseS, baseV, step) { + this.baseColor = new HSVColor(baseH, baseS, baseV); + this.currentH = baseH; + this.step = step; +} + +ColorIterator.prototype.next = function() { + var c = new HSVColor(this.currentH, this.baseColor.s, this.baseColor.v); + this.currentH += this.step; + + if (this.currentH >= 360) + this.currentH -= 360; + + return c; +}; + +function setBorderColor(ob, colorString) { + var s = ob.style; + s.borderBottomColor = colorString; + s.borderLeftColor = colorString; + s.borderRightColor = colorString; + s.borderTopColor = colorString; +} + + +function setBorderStyle(ob, borderStyle) { + var s= ob.style; + s.borderBottomStyle = borderStyle; + s.borderLeftStyle = borderStyle; + s.borderRightStyle = borderStyle; + s.borderTopStyle = borderStyle; +} + + +function setBorderWidth(ob, borderWidth) { + var s= ob.style; + s.borderBottomWidth = borderWidth; + s.borderLeftWidth = borderWidth; + s.borderRightWidth = borderWidth; + s.borderTopWidth = borderWidth; +} + +function zfill(number, digits) { + number = Math.floor(number); + var nStr = String(number); + while (nStr.length < digits) + nStr = "0" + nStr; + return nStr; +} + diff --git a/skins/color_utils.js.metadata b/skins/color_utils.js.metadata new file mode 100644 index 0000000..855fecc --- /dev/null +++ b/skins/color_utils.js.metadata @@ -0,0 +1,2 @@ +[default] +cache=HTTPCache diff --git a/skins/dd_trigger.js b/skins/dd_trigger.js new file mode 100644 index 0000000..b79f37e --- /dev/null +++ b/skins/dd_trigger.js @@ -0,0 +1,69 @@ +// (c) Benoît PIN 2006-2007 +// http://plinn.org +// Licence GPL + +function ddEventDispatch() { + addListener(document, 'mousedown', raisemousedown); + addListener(document, 'mousemove', raisemousemove); + addListener(document, 'mouseup', raisemouseup); +} + + +function getRectangleNode() { + var docid = document.id; + docid = document.id.slice(DocPrefixLength); + var frameId = IFramePrefix + docid; + iframe = window.parent.document.getElementById(frameId); + if (!iframe) + iframe = window.parent.document.getElementById("EpozEditor"); + return iframe.parentNode; +} + +var raisemousedown, raisemousemove, raisemouseup; + +if (browser.isIE55 || browser.isIE6up) { + raisemousedown = function() { + var rect = getRectangleNode(); + rect.fireEvent('onmousedown', window.event); + }; + + raisemousemove = function() { + var rect = getRectangleNode(); + rect.fireEvent('onmousemove', window.event); + rect.fireEvent('onmouseover', window.event); + }; + + raisemouseup = function() { + var rect = getRectangleNode(); + rect.fireEvent('onmouseup', window.event); + }; +} +else if (browser.isDOM2Event) { + // initMouseEvent("eventType", bubblesFlas, cancelableFlag, view, detailVal, screenX, screenY, clientX, clientY, ctrlKeyFlag, altKeyFlag, shiftKeyFlag, metaKeyFlag, buttonCode, relatedTargetNodeRef) + + raisemousedown = function(e) { + var event = document.createEvent("MouseEvents"); + var rect = getRectangleNode() + event.initMouseEvent("mousedown", true, true, window.parent, e.detail, e.screenX, e.screenY, e.clientX, e.clientY, e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, e.button, rect); + rect.dispatchEvent(event); + }; + + + raisemousemove = function(e) { + var overEvent = document.createEvent("MouseEvents"); + overEvent.initMouseEvent("mouseover", true, true, window.parent, e.detail, e.screenX, e.screenY, e.clientX, e.clientY, e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, e.button, rect); + var event = document.createEvent("MouseEvents"); + var rect = getRectangleNode() + event.initMouseEvent("mousemove", true, true, window.parent, e.detail, e.screenX, e.screenY, e.clientX, e.clientY, e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, e.button, rect); + rect.dispatchEvent(event); + rect.dispatchEvent(overEvent); + }; + + + raisemouseup = function(e) { + var event = document.createEvent("MouseEvents"); + var rect = getRectangleNode() + event.initMouseEvent("mouseup", true, true, window.parent, e.detail, e.screenX, e.screenY, e.clientX, e.clientY, e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, e.button, rect); + rect.dispatchEvent(event); + }; +} diff --git a/skins/dd_trigger.js.metadata b/skins/dd_trigger.js.metadata new file mode 100644 index 0000000..855fecc --- /dev/null +++ b/skins/dd_trigger.js.metadata @@ -0,0 +1,2 @@ +[default] +cache=HTTPCache diff --git a/skins/degrad_rectangle_header.gif b/skins/degrad_rectangle_header.gif new file mode 100644 index 0000000..721dc35 Binary files /dev/null and b/skins/degrad_rectangle_header.gif differ diff --git a/skins/delete_rectangle.gif b/skins/delete_rectangle.gif new file mode 100644 index 0000000..e70f5a9 Binary files /dev/null and b/skins/delete_rectangle.gif differ diff --git a/skins/delete_rectangle_hover.gif b/skins/delete_rectangle_hover.gif new file mode 100644 index 0000000..fba2ab6 Binary files /dev/null and b/skins/delete_rectangle_hover.gif differ diff --git a/skins/layout_controlers.js b/skins/layout_controlers.js new file mode 100644 index 0000000..c0a383e --- /dev/null +++ b/skins/layout_controlers.js @@ -0,0 +1,365 @@ +// (c) Benoît PIN 2006-2007 +// http://plinn.org +// Licence GPL + +// Global variables +var CURRENT_LAYER; +var LAYER_MANAGER; +var XML_OUTPUT; +var PLINN_DOCUMENT_URL; +var GLOBAL_DD_CONTROLER; + +function initPlinn(root_container, xmlDataUrl) { + PLINN_DOCUMENT_URL = document.getElementById("PlinnDocumentUrl").innerHTML; + form_path = PLINN_DOCUMENT_URL+'/'; // for epoz + initElementsPool(); + initWidgets(); + var imp = new XMLImport(xmlDataUrl, root_container); + GLOBAL_DD_CONTROLER = new GlobalPlinnDDControler(root_container); +} + + +function initWidgets() { + // Global control widgets + XML_OUTPUT = document.getElementById("xml_output") +} + +function initLayerManager(root_container) { + LAYER_MANAGER = new LayerManager(root_container, new ColorIterator(240, 80, 60, 83)); // todo : move in config file +} + +function Point(x, y) { + this.x = x; + this.y = y; +} +Point.prototype.difference = function(point) { return new Point(this.x - point.x, this.y - point.y); }; +Point.prototype.addition = function(point) { return new Point(this.x + point.x, this.y + point.y); }; +Point.prototype.toString = function() { return "(" + String(this.x) + ", " + String(this.y) + ")"; }; + + +function GlobalPlinnDDControler(root_container) { + this.root_container = root_container; + this.dragInProgress = false; + + this.currentRect = null; + this.dragAllowed = null; + this.resizeAllowed = null; + this.scalable = null; + this.deleteAllowed = null; + + + //Constants + this.DRAG_MODE = 0; + this.RESIZE_MODE = 1; + + this.mode = this.DRAG_MODE; + this.ddEventCaptureElmt = document; + + // event listeners + var thisControler = this; + with (this.root_container) { + onmouseover = function(evt){thisControler.onmouseover(evt);}; + onmouseout = function(evt){thisControler.onmouseout(evt);}; + } + with (this.ddEventCaptureElmt) { + onmousedown = function(evt){thisControler.onmousedown(evt);}; + onmousemove = function(evt){thisControler.onmousemove(evt);}; + onmouseup = function(evt){thisControler.onmouseup(evt);}; + } +} + +GlobalPlinnDDControler.prototype.onmouseover = function(evt) { + if (this.dragInProgress) return; + var ob = getTargetedObject(evt); + var rect = ob.rectangle; + if (!rect) return; + rect.showHandles(); +}; + +GlobalPlinnDDControler.prototype.onmouseout = function(evt) { + if (this.dragInProgress) return; + var ob = getTargetedObject(evt); + var rect = ob.rectangle; + if (!rect) return; + rect.hideHandles(); +}; + +GlobalPlinnDDControler.prototype.onmousedown = function(evt) { + var ob = getTargetedObject(evt); + var rect = ob.rectangle; + + if (!rect) return; + + this.currentRect = rect; + this.setDDOptions(rect.ddOptions); + + var evt = getEventObject(evt); + if (this.deleteAllowed && ( + (evt.shiftKey && evt.altKey) || + (ob.tagName == "IMG" && ob.className=="rectangle_delimg")) ) { + if (confirm(PlinnLang["Do you realy want to delete ?"])) { + var hostDiv = rect.hostDiv; + hostDiv.parentNode.removeChild(hostDiv); + } + return; + } + + this.originalClickPoint = new Point(evt.screenX, evt.screenY); + this.originalSize = new Point(rect.width, rect.height); + this.offsetPoint = rect.upperLeftCorner.difference(this.originalClickPoint); + + if (evt.shiftKey || (ob.tagName == "SPAN" && ob.className=="resize_handle")) { + this.mode = this.RESIZE_MODE; + this.startDrag(evt, rect); + } + else if(evt.altKey || (ob.tagName == "DIV" && ob.className=="rectangle_header")){ + this.mode = this.DRAG_MODE; + this.startDrag(evt, rect); + } +}; + +GlobalPlinnDDControler.prototype.onmousemove = function(evt) { + var evt = getEventObject(evt); + if (this.dragInProgress) { + var rectangle = this.currentRect; + if (this.dragAllowed && (this.mode == this.DRAG_MODE)) { + var relativeEvtPoint = new Point(evt.screenX, evt.screenY).addition(this.offsetPoint); + rectangle.moveTo(relativeEvtPoint); + } + else if (this.resizeAllowed && (this.mode == this.RESIZE_MODE)) { + var delta = new Point(evt.screenX, evt.screenY).difference(this.originalClickPoint); + var size = this.originalSize.addition(delta); + if (this.scalable) { + size = new Point(size.x, Math.round(size.x * rectangle.ratio)); + } + + rectangle.resizeTo(size); + } + if (browser.isIE) { + try {document.selection.clear();} + catch (e){return;} + } + } +}; + +GlobalPlinnDDControler.prototype.onmouseup = function(evt) { + this.drop() + var rectangle = this.currentRect; + if (rectangle && rectangle.ondrop) + rectangle.ondrop(); +}; + + +GlobalPlinnDDControler.prototype.startDrag = function(evt, rect) { + disablePropagation(evt); + disableDefault(evt); + this.dragInProgress = true; +}; + +GlobalPlinnDDControler.prototype.drop = function() {this.dragInProgress = false;}; + +GlobalPlinnDDControler.prototype.setDDOptions = function(options){ + this.dragAllowed = (options & 1) == 1; + this.resizeAllowed = (options & 2) == 2; + this.scalable = (options & 4) == 4; + this.deleteAllowed = (options & 8) == 8; +}; + + + +function LayerManager(main_space, colorIterator) { + this.space = main_space; + this.layers = new Array(); + this.defaultLayerWidth = 800; + this.defaultLayerHeight = 600; + this.colorIterator = colorIterator; + this.layerSelector = document.getElementById('layerSelector'); + var thisManager = this; + + // add layer selector commands + // separator between layers list and layer commandes + var sepOpt = document.createElement("option"); + sepOpt.innerHTML = "--------"; + sepOpt.disabled = "disabled"; + sepOpt.style.color = "#888"; + + // new layer command + var addLayerOpt = document.createElement("option"); + addLayerOpt.value = "add"; + addLayerOpt.innerHTML = PlinnLang["New layer"]; + addLayerOpt.style.color = "#000"; + + // remove layer command + var removeLayerOpt = document.createElement("option"); + removeLayerOpt.value = "rm"; + removeLayerOpt.innerHTML = PlinnLang["Remove layer"]; + removeLayerOpt.disabled = "disabled"; + + with (this.layerSelector) { + appendChild(sepOpt); + appendChild(addLayerOpt); + appendChild(removeLayerOpt); + } + addListener(this.layerSelector, 'change', function(){thisManager.activateLayer();}); +} + +LayerManager.prototype.addLayer = function(position) { + var color = this.colorIterator.next(); + var mode = 6; // resize and delete + if (this.layers.length <= 1) + mode = 2; // resize only + var l = new Rectangle(new Point(0, 0), this.defaultLayerWidth, this.defaultLayerHeight, "DIV_ELEMENT", 2); + setBorderColor(l, color.toRGBString()); + l.onresize = resizeOnMouseMove; + l.ondrop = resizeOnDrop; + l.style.overflow = "hidden"; + l.draw(this.space, position); + var index = this.layers.push(l) - 1; + + var opt = document.createElement("option"); + strId = String(index); + opt.value = strId; + opt.id = "layer_" + strId; + opt.innerHTML = PlinnLang["Layer "] + strId; + opt.style.color = color.toRGBString(); + this.layerSelector.insertBefore(opt, this.layerSelector.childNodes[this.layers.length-1]); + + // If there's only one layer, the remove command is disabled. + if (this.layers.length > 1) { + var rmCommand = this.layerSelector.childNodes[this.layerSelector.length -1] + rmCommand.removeAttribute("disabled"); + rmCommand.style.color="#000"; + } + this.activateLayer(index); +}; + +LayerManager.prototype.activateLayer = function(index) { + var layerSelector = this.layerSelector; + var index = (index != null) ? index : layerSelector.value; + // handle commands + index = ( Number(index) || Number(index ) == 0 ) ? Number(index) : index + + if (typeof(index) == typeof('')) { + switch (index) { + case "add" : + this.addLayer(); + break; + case "rm" : + this.removeCurrentLayer(); + break; + }; + return; + } + + CURRENT_LAYER = this.layers[index]; + this._syncLayerStatusImage(); + var opt = document.getElementById("layer_" + String(index)); + layerSelector.selectedIndex = opt.index; + layerSelector.style.color = opt.style.color; + this.putCurrentLayerOnTop(); +}; + + +LayerManager.prototype.putCurrentLayerOnTop = function() { + for(var i = 0 ; i < this.layers.length ; i++) { + layer = this.layers[i]; + if (layer == CURRENT_LAYER) + layer.style.zIndex = String(this.layers.length); + else + layer.style.zIndex = String(i); + } +}; + +LayerManager.prototype.toggleLayerVisibility = function(){ + var currentVisibility = CURRENT_LAYER.style.visibility; + if (currentVisibility == "hidden") + CURRENT_LAYER.style.visibility = "visible"; + else + CURRENT_LAYER.style.visibility = "hidden"; + + this._syncLayerStatusImage(); +}; + +LayerManager.prototype._syncLayerStatusImage = function() { + var layerStatusImage = document.getElementById("layer_status"); + if (CURRENT_LAYER.style.visibility == "hidden") { + layerStatusImage.src = "plinn_icons/hidden_layer.gif"; + layerStatusImage.title = PlinnLang["Show layer"]; + } + else { + layerStatusImage.src = "plinn_icons/visible_layer.gif"; + layerStatusImage.title = PlinnLang["Hide layer"]; + } +}; + +LayerManager.prototype.removeCurrentLayer = function() { + var layersLength = this.layers.length; + var layerSelector = this.layerSelector; + var layer, deletedLayerIndex; + for (var i = 0 ; i < layersLength ; i++) { + layer = this.layers[i]; + if (layer == CURRENT_LAYER) { + if (confirm(PlinnLang["confirm_layer_delete"] + i + "\" ?")) { + this.space.removeChild(layer.hostDiv); + this.layers.splice(i, 1); + layerSelector.removeChild(layerSelector.childNodes[i]); + deletedLayerIndex = i; + break; + } + else { + this.activateLayer(i); + return; + } + } + } + + // update layer indexes + for (var i = 0 ; i < this.layers.length ; i++) { + layerSelector.childNodes[i].value = String(i); + layerSelector.childNodes[i].id = "layer_" + String(i); + layerSelector.childNodes[i].innerHTML = PlinnLang["Layer "] + String(i); + } + + if (this.layers.length == 1) { + var removeCmdOpt = layerSelector.childNodes[layerSelector.childNodes.length - 1]; + removeCmdOpt.disabled = "disabled"; + removeCmdOpt.style.color = "#888"; + } + + if (deletedLayerIndex == 0) + this.layers[0].style.position = "relative"; + + // activate previous layer + this.activateLayer((deletedLayerIndex > 0) ? --deletedLayerIndex : 0); + +}; + +function resizeOnMouseMove() { + if (LAYER_MANAGER.layers.length > 5) + return; // to expensive + else + _resizeAllLayers(this); +} + +function resizeOnDrop() { _resizeAllLayers(this);} + +function _resizeAllLayers(layer) { + var layers = LAYER_MANAGER.layers; + var size = new Point(layer.width, layer.height); + var l; + for(var i = 0 ; i < layers.length ; i++) { + l = layers[i]; + if (layer != l) + l.resizeTo(size, true); + } + LAYER_MANAGER.defaultLayerWidth = size.x; + LAYER_MANAGER.defaultLayerHeight = size.y; +} + +function CreateAttachmentLink(data) { + // data like that : "title[attachment/a_file] + var title = data.split("\[", 1)[0]; + var url =data.match(/\[(.*)\]/)[1]; + var link ="" + title + ""; + InsertHTML(link); +} \ No newline at end of file diff --git a/skins/layout_controlers.js.metadata b/skins/layout_controlers.js.metadata new file mode 100644 index 0000000..855fecc --- /dev/null +++ b/skins/layout_controlers.js.metadata @@ -0,0 +1,2 @@ +[default] +cache=HTTPCache diff --git a/skins/layout_objects.js b/skins/layout_objects.js new file mode 100644 index 0000000..2d4ca89 --- /dev/null +++ b/skins/layout_objects.js @@ -0,0 +1,269 @@ +// (c) Benoît PIN 2006-2007 +// http://plinn.org +// Licence GPL + +var ELEMENTS_POOL = {}; + +function initElementsPool() { + // Element pool (for cloning) + ELEMENTS_POOL["DIV_ELEMENT"] = {"node" : document.getElementById("div"), + "beforeDraw" : null, + "getRawData" : null, + "putData" : null }; + + imgBeforeDraw = function() { + if (!this.node.src) { + var dispatch = document.getElementById("ImgDispatcher"); + var strRatio = dispatch.value.split("_", 1)[0]; + var src = dispatch.value.substring(strRatio.length+1); + this.ratio = new Number(strRatio).valueOf(); + this.node.src = PLINN_DOCUMENT_URL + '/' + src; + var w = this.width; + var size = (this.ratio > 1) ? + new Point(w, Math.round(w * this.ratio)) : + new Point(Math.round(w / this.ratio), w); + + this.resizeTo(size); + } + } + + ELEMENTS_POOL["IMG_ELEMENT"] = {"node" : document.getElementById("img"), + "beforeDraw" : imgBeforeDraw, + "getRawData" : function() { + var src = this.node.src; + src = src.split('/'); + return "attachments/" + src[src.length - 2] + "/getThumbnail"; + }, + "putData" : function(raw) { + this.node.src = PLINN_DOCUMENT_URL+"/attachments/"+raw; + } + }; + + epozBeforeDraw = function() { + var rect = this + var iframe = this.node.childNodes[0]; + var name = String(Math.floor(Math.random() * 10000)); + addListener(iframe, 'load', function(){initTextRectangle(iframe, name, rect);}); + + var ta = this.node.childNodes[1]; + ta.name = name; + ta.id = name; + } + + ELEMENTS_POOL["EPOZ_ELEMENT"] = {"node" : document.getElementById("epoz"), + "beforeDraw" : epozBeforeDraw, + "getRawData" : function() { + return this.node.childNodes[0].contentWindow.document.body.innerHTML; + }, + "putData" : function(raw) { + this.node.childNodes[1].value = raw; + } + }; + + for (var i = 0 ; i < ELEMENTS_POOL.length ; i++) + ELEMENTS_POOL[i].removeAttribute("id"); +} + +function Rectangle(upperLeftCorner, width, height, elementKey, ddOptions, ratio) { + this.meta_type = "Rectangle"; // ;-) + this.upperLeftCorner = upperLeftCorner; + this.width = width; + this.height = height; + this.elementKey = elementKey; + this.node = ELEMENTS_POOL[elementKey]["node"].cloneNode(true); + with (this.node.style) { + width="100%"; + height="100%"; + } + this.beforeDraw = ELEMENTS_POOL[elementKey]["beforeDraw"]; + this.getRawData = ELEMENTS_POOL[elementKey]["getRawData"]; + this.ddOptions = (!ddOptions) ? 11 : ddOptions; // 11-> drag, resize, delete + this.ratio = ratio; + + this.hostDiv = document.createElement("div"); + this.hostDiv.appendChild(this.node); + + this.resizeHandle = document.createElement("span"); + this.resizeHandle.className = "resize_handle"; + this.hostDiv.appendChild(this.resizeHandle); + + if((this.ddOptions & 8) == 8) { // move allowed + this.movDelHandle = document.createElement("div"); + this.movDelHandle.className = "rectangle_header"; + var delImg = document.createElement("img"); + delImg.src = "delete_rectangle.gif"; + delImg.className = "rectangle_delimg"; + delImg.onmouseover=function(){this.src="delete_rectangle_hover.gif";} + delImg.onmouseout=function(){this.src="delete_rectangle.gif";} + this.movDelHandle.appendChild(delImg); + this.hostDiv.appendChild(this.movDelHandle); + this.movDelHandle.rectangle = this; + delImg.rectangle = this; + + } + + // backward references + this.style = this.hostDiv.style; + this.node.rectangle = this; + this.hostDiv.rectangle = this; + this.resizeHandle.rectangle = this; +} + +Rectangle.prototype.showHandles = function() { + if (this.resizeHandle) + this.resizeHandle.style.visibility = "visible"; + if (this.movDelHandle) + this.movDelHandle.style.visibility = "visible"; +}; + +Rectangle.prototype.hideHandles = function() { + if (this.resizeHandle) + this.resizeHandle.style.visibility = "hidden"; + if (this.movDelHandle) + this.movDelHandle.style.visibility = "hidden"; +}; + +Rectangle.prototype._mac_moveDelHandle = function() { + // fix height + var epozWindow = this.node.childNodes[0].contentWindow; + if (epozWindow.scrollMaxY) + this.resizeHandle.style.right = "15px"; + else + this.resizeHandle.style.right = "0px"; + + // fix width + if (epozWindow.scrollMaxX) + this.resizeHandle.style.bottom = "15px"; + else + this.resizeHandle.style.bottom = "0px"; +}; + + +Rectangle.prototype.draw = function(container, position) { + if (this.beforeDraw) + this.beforeDraw(); + var style = this.style; + style.left = String(this.upperLeftCorner.x) + "px"; + style.top = String(this.upperLeftCorner.y) + "px"; + style.width = String(this.width) + "px"; + style.height = String(this.height) + "px"; + + style.display="block"; + var pos; + if (!position) + pos = "absolute"; + else + pos = position; + style.position = pos; + setBorderStyle(this, "solid"); + setBorderWidth(this, "1px"); + style.visibility = "inherit"; +/* style.overflow = "hidden";*/ + if (container.meta_type == "Rectangle") + setBorderColor(this, container.style.borderTopColor); + + container.appendChild(this.hostDiv); +}; + + +Rectangle.prototype.moveTo = function(upperLeftCorner) { + this.upperLeftCorner = upperLeftCorner; + var style = this.style; + style.left = String(this.upperLeftCorner.x) + "px"; + style.top = String(this.upperLeftCorner.y) + "px"; +}; + +Rectangle.prototype.resizeTo = function(size, cancelOnresize) { + if (size.x >= 10) + this.width = size.x; + if (size.y >= 10) + this.height = size.y; + + var style = this.style; + style.width = String(this.width) + "px"; + style.height = String(this.height) + "px"; + if (this.onresize && !cancelOnresize) + this.onresize(); +}; + +Rectangle.prototype.appendChild = function(node) { + this.node.appendChild(node); +}; + +Rectangle.prototype.setId = function(id) { + this.node.id = id; +}; + + + +function initTextRectangle(iframe, name, rect) { + var ta = iframe.nextSibling; // ta -> textarea + var data = ta.value; + + if (browser.isGecko) { + // Just a few cleanups for Mozilla + data = data.replace(//ig,''); + data = data.replace(/]*)>/ig,''); + data = data.replace(/<\/strong>/ig,''); + + data = data.replace(//ig,''); + data = data.replace(/]*)>/ig,''); + data = data.replace(/<\/em>/ig,''); + } + + // change iframe id + iframe.id = IFramePrefix + name; + iframe.style.border = "0px none transparent"; + + + iframe.contentWindow.document.body.innerHTML = data; + iframe.contentWindow.document.id = DocPrefix + name; + addListener(iframe, "click", handlePlinnEpozRedirect); + if (browser.isMac) { + addListener(iframe.contentWindow.document, 'overflow', function(){rect._mac_moveDelHandle();}); + addListener(iframe.contentWindow.document, 'underflow', function(){rect._mac_moveDelHandle();}); + } + + if (browser.isGecko) { + scriptExpr = 'EnableDesignMode("' + iframe.id + '");'; + window.setTimeout(scriptExpr, 10); + } +} + +// « overloads » + +function handlePlinnEpozRedirect(evt) { + redirectPlinnEpoz(getTargetedObject(evt)); +} + +function redirectPlinnEpoz(iframe) { + if(EpozElement) { + if (EpozElement == iframe) + return; + unwrapPlinnEpozVariables(); + } + + // update Epoz variables + wrapPlinnEpozVariables(iframe); + EpozElement.contentWindow.focus(); +} + +function wrapPlinnEpozVariables(iframe) { + fieldId = iframe.contentWindow.document.id.slice(DocPrefixLength); + + iframe.id = Epoz ; + EpozElement=iframe; + EpozTextArea = document.getElementById(fieldId); +} + + +function unwrapPlinnEpozVariables() { + if (!EpozElement) // no redirection happens yet. + return; + try { + fieldId = EpozElement.contentWindow.document.id.slice(DocPrefixLength); + } + catch (e) { } + + EpozElement.id = IFramePrefix + fieldId; +} diff --git a/skins/layout_objects.js.metadata b/skins/layout_objects.js.metadata new file mode 100644 index 0000000..855fecc --- /dev/null +++ b/skins/layout_objects.js.metadata @@ -0,0 +1,2 @@ +[default] +cache=HTTPCache diff --git a/skins/plinn_attachment_form.py b/skins/plinn_attachment_form.py new file mode 100644 index 0000000..090c57b --- /dev/null +++ b/skins/plinn_attachment_form.py @@ -0,0 +1,28 @@ +##parameters= +form = context.REQUEST.form +formId = form.get('formId') +epozInsert = form.get('epozInsert', False) +file = form.get('file', None) +fileOb = None + +if file : fileOb = context.addAttachment(file, formId) + +onload = None +if fileOb is not None : + if fileOb.meta_type == 'Photo' : + fileUrl = 'attachments/%s/getThumbnail' % fileOb.getId() + if epozInsert : + onload = "dispatchImageAndHideUploadForm('%s', '%s', true);" % (fileUrl, formId) + else : + ratio = float(fileOb.height)/fileOb.width + dispatchValue = "%f_%s" % (ratio, fileUrl) + onload = "dispatchImageAndHideUploadForm('%s','%s', false);" % (dispatchValue, formId) + else : + dispatchValue = '%s[%s]' % (fileOb.getId(), 'attachments/'+fileOb.getId()) + onload = "dispatchFileAndHideUploadForm('%s', '%s');" % (dispatchValue, formId) + +hideFormCode = "hideUploadForm('%s');" % formId + +return context.plinn_attachment_template(onload=onload, + hideFormCode=hideFormCode, + formId=formId) \ No newline at end of file diff --git a/skins/plinn_attachment_template.pt b/skins/plinn_attachment_template.pt new file mode 100644 index 0000000..062bfdc --- /dev/null +++ b/skins/plinn_attachment_template.pt @@ -0,0 +1,56 @@ + + + + + + + + + + + + + Image upload form + + + +

Insert image

+

Insert file

+
+ +
+ +
+ + + + \ No newline at end of file diff --git a/skins/plinn_blank_iframe.html.pt b/skins/plinn_blank_iframe.html.pt new file mode 100755 index 0000000..7029a91 --- /dev/null +++ b/skins/plinn_blank_iframe.html.pt @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/skins/plinn_doc.gif b/skins/plinn_doc.gif new file mode 100644 index 0000000..a92c9ae Binary files /dev/null and b/skins/plinn_doc.gif differ diff --git a/skins/plinn_doc.thumb.gif b/skins/plinn_doc.thumb.gif new file mode 100644 index 0000000..359ca22 Binary files /dev/null and b/skins/plinn_doc.thumb.gif differ diff --git a/skins/plinn_file_upload.gif b/skins/plinn_file_upload.gif new file mode 100644 index 0000000..b689dad Binary files /dev/null and b/skins/plinn_file_upload.gif differ diff --git a/skins/plinn_icons/hidden_layer.gif b/skins/plinn_icons/hidden_layer.gif new file mode 100644 index 0000000..8b85acb Binary files /dev/null and b/skins/plinn_icons/hidden_layer.gif differ diff --git a/skins/plinn_icons/imagebox.gif b/skins/plinn_icons/imagebox.gif new file mode 100644 index 0000000..cb90831 Binary files /dev/null and b/skins/plinn_icons/imagebox.gif differ diff --git a/skins/plinn_icons/layer.gif b/skins/plinn_icons/layer.gif new file mode 100644 index 0000000..018d3e1 Binary files /dev/null and b/skins/plinn_icons/layer.gif differ diff --git a/skins/plinn_icons/textarea.gif b/skins/plinn_icons/textarea.gif new file mode 100644 index 0000000..21822d6 Binary files /dev/null and b/skins/plinn_icons/textarea.gif differ diff --git a/skins/plinn_icons/visible_layer.gif b/skins/plinn_icons/visible_layer.gif new file mode 100644 index 0000000..bde835c Binary files /dev/null and b/skins/plinn_icons/visible_layer.gif differ diff --git a/skins/plinn_image_upload.gif b/skins/plinn_image_upload.gif new file mode 100644 index 0000000..9020ace Binary files /dev/null and b/skins/plinn_image_upload.gif differ diff --git a/skins/plinn_init_javascript_code.dtml b/skins/plinn_init_javascript_code.dtml new file mode 100644 index 0000000..f83f7e8 --- /dev/null +++ b/skins/plinn_init_javascript_code.dtml @@ -0,0 +1,8 @@ + diff --git a/skins/plinn_macros.pt b/skins/plinn_macros.pt new file mode 100644 index 0000000..fd79963 --- /dev/null +++ b/skins/plinn_macros.pt @@ -0,0 +1,66 @@ + + + + + + + Plinn macros + + + +
+ +BoldItalicUnderlineStrikethrough SubscriptSuperscript RemoveFormat TextColorBackColor AlignLeftCenterAlignRight OrderedListUnorderedListOutdentIndent + +InsertImage
+
+
+ +InsertFile
+
+
+ +InsertLinkInsertAnchor +InsertRuleInsertTable UndoRedo +
+ + \ No newline at end of file diff --git a/skins/plinn_multilingual.js.pt b/skins/plinn_multilingual.js.pt new file mode 100644 index 0000000..bd0d5c7 --- /dev/null +++ b/skins/plinn_multilingual.js.pt @@ -0,0 +1,17 @@ +var PlinnLang=[]; + +PlinnLang[""]=""; + \ No newline at end of file diff --git a/skins/plinn_multilingual.js.pt.metadata b/skins/plinn_multilingual.js.pt.metadata new file mode 100644 index 0000000..855fecc --- /dev/null +++ b/skins/plinn_multilingual.js.pt.metadata @@ -0,0 +1,2 @@ +[default] +cache=HTTPCache diff --git a/skins/plinndocument_edit_control.py b/skins/plinndocument_edit_control.py new file mode 100644 index 0000000..a2e55b6 --- /dev/null +++ b/skins/plinndocument_edit_control.py @@ -0,0 +1,15 @@ +## Script (Python) "plinndocument_edit" +##parameters=text='', file='', SafetyBelt='', **kw +##title=Edit a document +## + +from Products.CMFDefault.exceptions import EditingConflict, ResourceLockedError + +if text != context.text : + try: + context.edit( 'html', text, file, safety_belt = SafetyBelt) + return context.setStatus(True, 'Document changed.') + except (ResourceLockedError, EditingConflict), msg: + return context.setStatus(False, msg) +else : + return context.setStatus(False, 'Nothing to change.') diff --git a/skins/plinndocument_edit_form.py b/skins/plinndocument_edit_form.py new file mode 100644 index 0000000..5484bcf --- /dev/null +++ b/skins/plinndocument_edit_form.py @@ -0,0 +1,13 @@ +##parameters=change='', change_and_view='', ajax='' + +form = context.REQUEST.form +if change and \ + context.plinndocument_edit_control(**form) and \ + context.setRedirect(context, 'object/edit', ajax=ajax) : + return +elif change_and_view and \ + context.plinndocument_edit_control(**form) and \ + context.setRedirect(context, 'object/view', ajax=ajax) : + return + +return context.plinndocument_edit_template() diff --git a/skins/plinndocument_edit_template.pt b/skins/plinndocument_edit_template.pt new file mode 100644 index 0000000..ef54386 --- /dev/null +++ b/skins/plinndocument_edit_template.pt @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + +
+
generic_header macro
+
+ +
+ + + Add text    + + + + + Toggle layer visibility + +
+ + +
+ + + +
+ + +
+
+ +
+ + diff --git a/skins/plinndocument_view.pt b/skins/plinndocument_view.pt new file mode 100644 index 0000000..c14b64d --- /dev/null +++ b/skins/plinndocument_view.pt @@ -0,0 +1,13 @@ + + + + + Plinn document edit + + + +

+ + + \ No newline at end of file diff --git a/skins/resize_handle.png b/skins/resize_handle.png new file mode 100644 index 0000000..2ecfaa1 Binary files /dev/null and b/skins/resize_handle.png differ diff --git a/skins/xml_io.js b/skins/xml_io.js new file mode 100644 index 0000000..bfd7cf0 --- /dev/null +++ b/skins/xml_io.js @@ -0,0 +1,132 @@ +// (c) Benoît PIN 2006-2007 +// http://plinn.org +// Licence GPL + +function XMLExport() { + this.domDoc = Sarissa.getDomDocument('http://plinn.org/namespaces/plinn_document/1.0', 'plinn'); + this.rootNode = this.domDoc.documentElement; +} + +XMLExport.prototype.getXML = function() { + this.exportDocument(); + var s = new XMLSerializer(); + XML_OUTPUT.value = s.serializeToString(this.domDoc); +}; + + +XMLExport.prototype.exportDocument = function() { + this.exportRectangles(LAYER_MANAGER.space, this.rootNode); +}; + +XMLExport.prototype.exportRectangles = function(baseObj, baseNode) { + var doc = this.domDoc; + var childs = baseObj.childNodes; + + for(var i = 0 ; i < childs.length ; i++) { + rectObj = childs[i].rectangle; + if (!rectObj) + continue; + + // rectangle + var rectEl = doc.createElement("rectangle"); + rectEl.setAttribute("width", rectObj.width); + rectEl.setAttribute("height", rectObj.height); + rectEl.setAttribute("elementKey", rectObj.elementKey); + rectEl.setAttribute("ddOptions", rectObj.ddOptions); + rectEl.setAttribute("ratio", rectObj.ratio); + rectEl.setAttribute("visibility", rectObj.style.visibility); + + // upperLeftCorner + var ulc = doc.createElement("upperLeftCorner"); + var point = doc.createElement("point"); + point.setAttribute("x", rectObj.upperLeftCorner.x); + point.setAttribute("y", rectObj.upperLeftCorner.y); + ulc.appendChild(point); + rectEl.appendChild(ulc); + + // raw data + var rdata = doc.createElement("rawData"); + if (rectObj.getRawData) { + var rawEl = doc.createTextNode(rectObj.getRawData()); + rdata.appendChild(rawEl); + } + rectEl.appendChild(rdata); + + baseNode.appendChild(rectEl); + + this.exportRectangles(rectObj.node, rectEl); + } +}; + +function XMLImport(url, root_container) { + this.root_container = root_container; + var thisImporter = this; + var req = new XMLHttpRequest(); + + req.onreadystatechange = function() { + if(req.readyState == 4) + thisImporter.constructDocument(req); + } + req.open("GET", url, true); + req.send(null); +} + +XMLImport.prototype.constructDocument = function(req) { + var rootNode = req.responseXML.documentElement; + var layerElements = rootNode.childNodes; + initLayerManager(this.root_container, true); + var layerElement; + for (var i = 0 ; i < layerElements.length ; i++) { + layerElement = layerElements[i]; + if (i==0) { // initialize LAYER_MANAGER from first layer data + LAYER_MANAGER.defaultLayerWidth = parseInt(layerElement.getAttribute("width")); + LAYER_MANAGER.defaultLayerHeight = parseInt(layerElement.getAttribute("height")); + LAYER_MANAGER.addLayer("relative"); + } + else + LAYER_MANAGER.addLayer(); + + // common part + if (layerElement.getAttribute("visibility") == "hidden") + LAYER_MANAGER.toggleLayerVisibility(); + + this.constructRectangles(CURRENT_LAYER, layerElement) + } +}; + +XMLImport.prototype.constructRectangles = function(baseObj, baseNode) { + var rectangleElements = baseNode.childNodes; + var rectE, rect, ulcE, ulc, rawDataE, rawData, putFunc; + + for (var i = 0 ; i < rectangleElements.length ; i ++) { + rectE = rectangleElements[i]; + if (rectE.nodeName != "rectangle") + continue; + ulcE = rectE.childNodes[0].childNodes[0]; + rawDataE = rectE.childNodes[1] + + ulc = new Point( parseInt(ulcE.getAttribute("x")), parseInt(ulcE.getAttribute("y")) ) + rect = new Rectangle(ulc, + parseInt(rectE.getAttribute("width")), + parseInt(rectE.getAttribute("height")), + rectE.getAttribute("elementKey"), + parseInt(rectE.getAttribute("ddOptions")), + parseFloat(rectE.getAttribute("ratio"))); + + putFunc = ELEMENTS_POOL[rectE.getAttribute("elementKey")]["putData"] + if (putFunc) + putFunc.apply(rect, [rawDataE.childNodes[0].nodeValue]); + + rect.draw(baseObj); + } +}; + +/* utils */ +function _plinnDocumentBeforeSubmit() { + with (GLOBAL_DD_CONTROLER.ddEventCaptureElmt) { + onmousedown=null; + onmousemouse=null; + onmouseup=null; + } + new XMLExport().getXML(); +} \ No newline at end of file diff --git a/skins/xml_io.js.metadata b/skins/xml_io.js.metadata new file mode 100644 index 0000000..855fecc --- /dev/null +++ b/skins/xml_io.js.metadata @@ -0,0 +1,2 @@ +[default] +cache=HTTPCache