Importation initiale. master
authorBenoît Pin <benoit.pin@gmail.com>
Sun, 7 Nov 2010 02:08:56 +0000 (03:08 +0100)
committerBenoît Pin <benoit.pin@gmail.com>
Sun, 7 Nov 2010 02:08:56 +0000 (03:08 +0100)
45 files changed:
HistoryAdapters.py [new file with mode: 0755]
PlinnDocument.py [new file with mode: 0644]
__init__.py [new file with mode: 0644]
configure.zcml [new file with mode: 0644]
graphics/delete_rectangle.psd [new file with mode: 0644]
graphics/layer_status.psd [new file with mode: 0644]
graphics/resize_handle.psd [new file with mode: 0644]
interfaces.py [new file with mode: 0755]
license.txt [new file with mode: 0755]
profiles/default/types.xml [new file with mode: 0644]
profiles/default/types/Plinn_Document.xml [new file with mode: 0644]
skins/color_utils.js [new file with mode: 0644]
skins/color_utils.js.metadata [new file with mode: 0644]
skins/dd_trigger.js [new file with mode: 0644]
skins/dd_trigger.js.metadata [new file with mode: 0644]
skins/degrad_rectangle_header.gif [new file with mode: 0644]
skins/delete_rectangle.gif [new file with mode: 0644]
skins/delete_rectangle_hover.gif [new file with mode: 0644]
skins/layout_controlers.js [new file with mode: 0644]
skins/layout_controlers.js.metadata [new file with mode: 0644]
skins/layout_objects.js [new file with mode: 0644]
skins/layout_objects.js.metadata [new file with mode: 0644]
skins/plinn_attachment_form.py [new file with mode: 0644]
skins/plinn_attachment_template.pt [new file with mode: 0644]
skins/plinn_blank_iframe.html.pt [new file with mode: 0755]
skins/plinn_doc.gif [new file with mode: 0644]
skins/plinn_doc.thumb.gif [new file with mode: 0644]
skins/plinn_file_upload.gif [new file with mode: 0644]
skins/plinn_icons/hidden_layer.gif [new file with mode: 0644]
skins/plinn_icons/imagebox.gif [new file with mode: 0644]
skins/plinn_icons/layer.gif [new file with mode: 0644]
skins/plinn_icons/textarea.gif [new file with mode: 0644]
skins/plinn_icons/visible_layer.gif [new file with mode: 0644]
skins/plinn_image_upload.gif [new file with mode: 0644]
skins/plinn_init_javascript_code.dtml [new file with mode: 0644]
skins/plinn_macros.pt [new file with mode: 0644]
skins/plinn_multilingual.js.pt [new file with mode: 0644]
skins/plinn_multilingual.js.pt.metadata [new file with mode: 0644]
skins/plinndocument_edit_control.py [new file with mode: 0644]
skins/plinndocument_edit_form.py [new file with mode: 0644]
skins/plinndocument_edit_template.pt [new file with mode: 0644]
skins/plinndocument_view.pt [new file with mode: 0644]
skins/resize_handle.png [new file with mode: 0644]
skins/xml_io.js [new file with mode: 0644]
skins/xml_io.js.metadata [new file with mode: 0644]

diff --git a/HistoryAdapters.py b/HistoryAdapters.py
new file mode 100755 (executable)
index 0000000..add0e8b
--- /dev/null
@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+#######################################################################################
+#   Plinn - http://plinn.org                                                          #
+#   Copyright © 2005-2009  Benoît PIN <benoit.pin@ensmp.fr>                           #
+#                                                                                     #
+#   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 (file)
index 0000000..3267069
--- /dev/null
@@ -0,0 +1,273 @@
+# -*- coding: utf-8 -*-
+#######################################################################################
+#   Plinn - http://plinn.org                                                          #
+#   Copyright (C) 2005-2007  Benoît PIN <benoit.pin@ensmp.fr>                         #
+#                                                                                     #
+#   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('<img(.*?)>', 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('<img(.*?)src(.*?)=(.*?)"(?P<src>(.*?))"(.*?)>|<a(.*?)href(.*?)=(.*?)"(?P<href>(.*?))"(.*?)>', re.IGNORECASE)
+EMPTY_PLINN_DOCUMENT = '<plinn><rectangle width="800" height="600" elementKey="DIV_ELEMENT" ddOptions="2" ratio="undefined" visibility="visible"><upperLeftCorner><point x="0" y="0"/></upperLeftCorner><rawData/></rectangle></plinn>'
+
+
+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('<div class="plinn_document"/>')
+                               htmlDomDoc = htmlDom.documentElement
+                               
+                               self._transformRectangles(plinnElement, htmlDomDoc)
+                               firstChildStyle = htmlDomDoc.firstChild.getAttribute('style')
+                               htmlDomDoc.setAttribute('style', firstChildStyle.replace('absolute', 'relative'))
+                               
+                               if mergeLayers :
+                                       mergedDom = minidom.parseString('<div class="plinn_document"/>')
+                                       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('&lt;', '<').replace('&gt;', '>').replace('&quot;', '"').replace('&amp;', '&')
+                               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 '<img src="%(src)s/getThumbnail" width="%(width)s" height="%(height)s" alt="%(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 (file)
index 0000000..a2dc2a1
--- /dev/null
@@ -0,0 +1,35 @@
+# -*- coding: utf-8 -*-
+#######################################################################################
+#   Plinn - http://plinn.org                                                          #
+#   Copyright (C) 2005-2007  Benoît PIN <benoit.pin@ensmp.fr>                         #
+#                                                                                     #
+#   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 (file)
index 0000000..e72308a
--- /dev/null
@@ -0,0 +1,32 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:five="http://namespaces.zope.org/five"
+    xmlns:cmf="http://namespaces.zope.org/cmf"
+    xmlns:genericsetup="http://namespaces.zope.org/genericsetup">
+
+  <cmf:registerDirectory directory="skins" name="plinn_document" recursive="True"/>
+
+  <five:registerClass
+      class=".PlinnDocument.PlinnDocument"
+      meta_type="Plinn Document"
+      permission="cmf.AddPortalContent"
+      />
+  <utility
+      component=".PlinnDocument.PlinnDocumentFactory"
+      name="plinndocument"
+      />
+  
+  <genericsetup:registerProfile
+      name="default"
+      title="Plinn Document"
+      description="Adds Plinn document content type."
+      provides="Products.GenericSetup.interfaces.EXTENSION"
+      for="Products.CMFCore.interfaces.ISiteRoot"
+      />
+  
+  <adapter
+    provides="Products.Plinn.interfaces.IContentHistory"
+    for=".interfaces.IPlinnDocument"
+    factory=".HistoryAdapters.PlinnDocumentHistory"/>
+
+</configure>
\ No newline at end of file
diff --git a/graphics/delete_rectangle.psd b/graphics/delete_rectangle.psd
new file mode 100644 (file)
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 (file)
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 (file)
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 (executable)
index 0000000..ca0edbd
--- /dev/null
@@ -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 (executable)
index 0000000..3912109
--- /dev/null
@@ -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.
+\f
+                   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.)
+\f
+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.
+\f
+  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.
+\f
+  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
+\f
+           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.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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.
+
+  <signature of Ty Coon>, 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 (file)
index 0000000..04e5f83
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0"?>
+<object name="portal_types" meta_type="CMF Types Tool">
+  <object name="Plinn Document" meta_type="Factory-based Type Information"/>
+</object>
diff --git a/profiles/default/types/Plinn_Document.xml b/profiles/default/types/Plinn_Document.xml
new file mode 100644 (file)
index 0000000..bce5392
--- /dev/null
@@ -0,0 +1,42 @@
+<?xml version="1.0"?>
+<object name="Plinn Document" meta_type="Factory-based Type Information"
+   i18n:domain="plinn" xmlns:i18n="http://xml.zope.org/namespaces/i18n">
+  <property name="title">Plinn Document</property>
+  <property name="description">Plinn Documents contain text and images that can be layout with  WYSIWYG and drag&amp;drop editor.</property>
+  <property name="content_icon">plinn_doc.gif</property>
+  <property name="content_meta_type">Plinn Document</property>
+  <property name="product"></property>
+  <property name="factory">plinndocument</property>
+  <property name="immediate_view">object/edit</property>
+  <property name="global_allow">True</property>
+  <property name="filter_content_types">True</property>
+  <property name="allowed_content_types"></property>
+  <property name="allow_discussion">False</property>
+  <alias from="info" to="content_info"/>
+  <alias from="gethtml" to="source_html"/>
+  <alias from="(Default)" to="plinndocument_view"/>
+  <alias from="view" to="plinndocument_view"/>
+  <action action_id="view" title="View" condition_expr=""
+          url_expr="string:${object_url}/plinndocument_view"
+          category="object" visible="True">
+   <permission value="View"/>
+  </action>
+  <action action_id="edit" title="Edit" condition_expr=""
+          url_expr="string:${object_url}/plinndocument_edit_form"
+          category="object" visible="True">
+   <permission value="Modify portal content"/>
+  </action>
+  <action action_id="localroles" title="Share"
+          condition_expr=""
+          url_expr="string:${object_url}/folder_localrole_form"
+          category="object" visible="True">
+   <permission value="Set Local Roles"/>
+  </action>
+  <action action_id="history" title="Status history"
+          condition_expr=""
+          url_expr="string:${object_url}/content_status_history"
+          category="object" visible="True">
+   <permission value="Request review"/>
+   <permission value="Review portal content"/>
+  </action>
+</object>
diff --git a/skins/color_utils.js b/skins/color_utils.js
new file mode 100644 (file)
index 0000000..7c70d07
--- /dev/null
@@ -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 (file)
index 0000000..855fecc
--- /dev/null
@@ -0,0 +1,2 @@
+[default]
+cache=HTTPCache
diff --git a/skins/dd_trigger.js b/skins/dd_trigger.js
new file mode 100644 (file)
index 0000000..b79f37e
--- /dev/null
@@ -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 (file)
index 0000000..855fecc
--- /dev/null
@@ -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 (file)
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 (file)
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 (file)
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 (file)
index 0000000..c0a383e
--- /dev/null
@@ -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 ="<a href=\"" + url + "\">" + title + "</a>";
+       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 (file)
index 0000000..855fecc
--- /dev/null
@@ -0,0 +1,2 @@
+[default]
+cache=HTTPCache
diff --git a/skins/layout_objects.js b/skins/layout_objects.js
new file mode 100644 (file)
index 0000000..2d4ca89
--- /dev/null
@@ -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(/<strong>/ig,'<b>');
+               data = data.replace(/<strong(\s[^>]*)>/ig,'<b$1>');
+               data = data.replace(/<\/strong>/ig,'</b>');
+               
+               data = data.replace(/<em>/ig,'<i>');
+               data = data.replace(/<em(\s[^>]*)>/ig,'<i$1>');
+               data = data.replace(/<\/em>/ig,'</i>');
+       }
+
+       // 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 (file)
index 0000000..855fecc
--- /dev/null
@@ -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 (file)
index 0000000..090c57b
--- /dev/null
@@ -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 (file)
index 0000000..062bfdc
--- /dev/null
@@ -0,0 +1,56 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml"
+                       tal:define="portal_url nocall:here/portal_url;
+                                                                       dummy python:request.RESPONSE.setHeader('Content-Type', 'text/html;;charset=utf-8')">
+
+  <head>
+    <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
+      <script type="text/javascript" defer="defer" tal:attributes="src here/epoz_script_detect.js/absolute_url"></script>
+      <script type="text/javascript" defer="defer" tal:attributes="src here/javascript_events_api.js/absolute_url"></script>
+      <link href="." rel="stylesheet" media="screen" tal:attributes="href here/zpt_stylesheet.css/absolute_url" />
+      <link href="." rel="stylesheet" media="screen" tal:attributes="href string:${portal_url}/plinn_style.css" />
+      <style>
+        body {background:#ccc;}
+      </style>
+      <script type="text/javascript">
+      //<!--
+      function hideUploadForm(formId) {
+       window.parent.document.getElementById(formId).style.visibility = "hidden" ;
+      }
+      
+      function dispatchImageAndHideUploadForm(imgUrl, formId, epozInsert) {
+       var dispatcherId = (epozInsert) ? "EpozImgDispatcher" : "ImgDispatcher";
+       var dispatch = window.parent.document.getElementById(dispatcherId);
+       dispatch.value = imgUrl;
+       raiseMouseEvent(dispatch, 'click');
+                       window.parent.document.getElementById(formId).style.visibility = "hidden" ;
+      }
+      
+      function dispatchFileAndHideUploadForm(data, formId) {
+       var dispatch = window.parent.document.getElementById("AttachmentDispatcher");
+       dispatch.value = data;
+       raiseMouseEvent(dispatch, 'click');
+                       window.parent.document.getElementById(formId).style.visibility = "hidden" ;
+      }
+      //-->
+      </script>
+    <title>Image upload form</title>
+  </head>
+
+  <body tal:attributes="onload options/onload"
+                        i18n:domain="plinn">
+    <h3 i18n:translate="" tal:condition="python:options['formId']=='ImageUploadForm'">Insert image</h3>
+    <h3 i18n:translate="" tal:condition="python:options['formId']=='FileUploadForm'">Insert file</h3>
+    <form action="plinn_attachment_form" method="post" enctype="multipart/form-data">
+       <input type="hidden" name="formId" tal:attributes="value options/formId" />
+      <input type="file" name="file" size="16"/><input type="submit" name="upload" value="Upload" i18n:attributes="value" /><br>
+      <label tal:condition="python:options['formId']=='ImageUploadForm'">
+       <input type="checkbox" name="epozInsert:bool" value="True" />
+       <span i18n:translate="" tal:omit-tag="">Insert inside text</span>
+      </label>
+    </form>
+    <button onclick="hideUploadForm();" i18n:translate="" tal:attributes="onclick options/hideFormCode">Cancel</button>
+  </body>
+
+</html>
\ 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 (executable)
index 0000000..7029a91
--- /dev/null
@@ -0,0 +1,43 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns:tal="http://xml.zope.org/namespaces/tal"
+xmlns:metal="http://xml.zope.org/namespaces/metal">
+<head>
+<base tal:attributes="href python:here.absolute_url()+'/'" />
+<meta http-equiv="Content-Type"
+          tal:define="dummy python:request.RESPONSE.setHeader('Content-Type', 'text/html;; charset=%s' % request.get('charset','utf-8'))"
+          tal:attributes="content python: 'text/html;; charset=%s' % request.get('charset','utf-8')" />
+<style media="all" type="text/css">
+a {
+       text-decoration:none;
+       border-bottom:1px solid #369;
+}
+<!--
+ a[name] {
+     border:1px solid grey;
+     background: lightgrey url(misc_/Epoz/epoz_button_anchor.gif) -2px -3px no-repeat;
+     padding: 1px 0px 1px 20px;
+     margin: 5px;
+ }
+
+ a[name]:after {
+     content: attr(name);
+ }
+-->
+</style>
+<style tal:condition="python: request.get('css')" type="text/css" media="all"
+          tal:content="string:@import url(${request/css});"></style>
+<style tal:condition="python: request.get('customcss')" type="text/css" media="all"
+          tal:content="string:@import url(${request/customcss});"></style>
+<script type="text/javascript" tal:attributes="src here/epoz_script_detect.js/absolute_url"></script>
+<script type="text/javascript" tal:attributes="src here/epoz_iframe_trigger.js/absolute_url"></script>
+<script type="text/javascript" tal:attributes="src here/javascript_events_api.js/absolute_url"></script>
+<script type="text/javascript" tal:attributes="src here/dd_trigger.js/absolute_url"></script>
+<script type="text/javascript"><!--
+       ddEventDispatch();
+//-->
+</script>
+</head>
+<body class="plinn_document" contentEditable="true">
+</body>
+</html>
\ No newline at end of file
diff --git a/skins/plinn_doc.gif b/skins/plinn_doc.gif
new file mode 100644 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
index 0000000..f83f7e8
--- /dev/null
@@ -0,0 +1,8 @@
+<!--
+    function init() {
+       initPlinn(document.getElementById("&dtml-main_space;"), "&dtml-xmlDataUrl;");
+    }
+    //window.addEventListener("load", init, true);
+    
+//
+-->
diff --git a/skins/plinn_macros.pt b/skins/plinn_macros.pt
new file mode 100644 (file)
index 0000000..fd79963
--- /dev/null
@@ -0,0 +1,66 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+  <head>
+    <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
+    <title>Plinn macros</title>
+  </head>
+
+  <body>
+<div id="EpozToolbar" metal:define-macro="EpozToolbar" i18n:domain="epoz">
+<select id="formatblock" onchange="SelectFormat(this)" style="margin-bottom: 2px" >
+  <option value="" i18n:translate="">Normal</option>
+  <option value="<p>" i18n:translate="">Paragraph</option>
+  <option value="<h1>" i18n:translate="">Heading1</option>
+  <option value="<h2>" i18n:translate="">Heading2</option>
+  <option value="<h3>" i18n:translate="">Heading3</option>
+  <option value="<h4>" i18n:translate="">Heading4</option>
+  <option value="<h5>" i18n:translate="">Heading5</option>
+  <option value="<h6>" i18n:translate="">Heading6</option>
+  <option value="<pre>" i18n:translate="">Formatted</option>
+</select>
+<img  tal:attributes="src here/epoz_button_bold.gif/absolute_url" width="23" height="22" alt="Bold" title="Bold" onclick="FormatText('bold', '');" i18n:attributes="alt ; title"
+/><img  tal:attributes="src here/epoz_button_italic.gif/absolute_url" width="23" height="22" alt="Italic" title="Italic" onclick="FormatText('italic', '');" i18n:attributes="alt ; title"
+/><img  tal:attributes="src here/epoz_button_underline.gif/absolute_url" width="23" height="22" alt="Underline" title="Underline" onclick="FormatText('underline', '');" i18n:attributes="alt ; title"
+/><img  tal:attributes="src here/epoz_button_strikethrough.gif/absolute_url" width="23" height="22" alt="Strikethrough" title="Strikethrough" onclick="FormatText('strikethrough', '');" i18n:attributes="alt ; title"
+/>&nbsp;<img  tal:attributes="src here/epoz_button_subscript.gif/absolute_url" width="23" height="22" alt="Subscript" title="Subscript" onclick="FormatText('subscript', '');" i18n:attributes="alt ; title"
+/><img  tal:attributes="src here/epoz_button_superscript.gif/absolute_url" width="23" height="22" alt="Superscript" title="Superscript" onclick="FormatText('superscript', '');" i18n:attributes="alt ; title"
+/>&nbsp;<img  tal:attributes="src here/epoz_button_unformat.gif/absolute_url" width="23" height="22" alt="RemoveFormat" title="RemoveFormat" onclick="FormatText('removeformat', '');" i18n:attributes="alt ; title"
+/>&nbsp;<img  tal:attributes="src here/epoz_button_textcolor.gif/absolute_url" width="23" height="22" alt="TextColor" title="TextColor" onclick="SetTextColor();" i18n:attributes="alt ; title"
+/><img  tal:attributes="src here/epoz_button_bgcolor.gif/absolute_url" width="23" height="22" alt="BackColor" title="BackColor" onclick="SetBackColor();" i18n:attributes="alt ; title"
+/>&nbsp;<img  tal:attributes="src here/epoz_button_left_just.gif/absolute_url" width="23" height="22" alt="AlignLeft" title="AlignLeft" onclick="FormatText('justifyleft', '');" i18n:attributes="alt ; title"
+/><img  tal:attributes="src here/epoz_button_centre.gif/absolute_url" width="23" height="22" alt="Center" title="Center" onclick="FormatText('justifycenter', '');" i18n:attributes="alt ; title"
+/><img  tal:attributes="src here/epoz_button_right_just.gif/absolute_url" width="23" height="22" alt="AlignRight" title="AlignRight" onclick="FormatText('justifyright', '');" i18n:attributes="alt ; title"
+/>&nbsp;<img  tal:attributes="src here/epoz_button_numbered_list.gif/absolute_url" width="23" height="22" alt="OrderedList" title="OrderedList" onclick="FormatText('insertorderedlist', '');" i18n:attributes="alt ; title"
+/><img  tal:attributes="src here/epoz_button_list.gif/absolute_url" width="23" height="22" alt="UnorderedList" title="UnorderedList" onclick="FormatText('insertunorderedlist', '');" i18n:attributes="alt ; title"
+/><img  tal:attributes="src here/epoz_button_outdent.gif/absolute_url" width="23" height="22" alt="Outdent" title="Outdent" onclick="FormatText('outdent', '');" i18n:attributes="alt ; title"
+/><img  tal:attributes="src here/epoz_button_indent.gif/absolute_url" width="23" height="22" alt="Indent" title="Indent" onclick="FormatText('indent', '');" i18n:attributes="alt ; title"
+/>
+
+<img  tal:attributes="src here/plinn_image_upload.gif/absolute_url" width="23" height="22" alt="InsertImage" title="InsertImage"
+onclick="document.getElementById('ImageUploadForm').style.visibility='visible';" i18n:attributes="alt ; title"
+/><span style="position:relative;vertical-align:top;z-index:1000"><span id="ImageUploadForm"><div style="position:relative;vertical-align:top"><div
+  style="position:absolute;width:23px;height:7px;top:-9px;left:288px;background:#ccc;border-left: 1px solid #aaa;border-right: 1px solid #aaa;float:left"><!-- --></div></div>
+  <iframe tal:attributes="src string:${here/absolute_url}/plinn_attachment_form?formId=ImageUploadForm"
+                        style="width:100% ; height:100% ; border:none" frameborder="0"></iframe></span>
+</span>
+
+<img  tal:attributes="src here/plinn_file_upload.gif/absolute_url" width="23" height="22" alt="InsertFile" title="InsertFile"
+onclick="document.getElementById('FileUploadForm').style.visibility='visible';" i18n:attributes="alt ; title"
+/><span style="position:relative;vertical-align:top;z-index:1000"><span id="FileUploadForm"><div style="position:relative;vertical-align:top"><div
+  style="position:absolute;width:23px;height:7px;top:-9px;left:288px;background:#ccc;border-left: 1px solid #aaa;border-right: 1px solid #aaa;float:left"><!-- --></div></div>
+  <iframe tal:attributes="src string:${here/absolute_url}/plinn_attachment_form?formId=FileUploadForm"
+                        style="width:100% ; height:100% ; border:none" frameborder="0"></iframe></span>
+</span>
+
+<img  tal:attributes="src here/epoz_button_hyperlink.gif/absolute_url" width="23" height="22" alt="InsertLink" title="InsertLink" onclick="CreateLink();"  i18n:attributes="alt ; title"
+/><img  tal:attributes="src here/epoz_button_anchor.gif/absolute_url" width="23" height="22" alt="InsertAnchor" title="InsertAnchor" onclick="CreateAnchor();"  i18n:attributes="alt ; title"
+/>
+<img  tal:attributes="src here/epoz_button_hr.gif/absolute_url" width="23" height="22" alt="InsertRule" title="InsertRule" onclick="FormatText('inserthorizontalrule', '');" i18n:attributes="alt ; title"
+/><img  tal:attributes="src here/epoz_button_table.gif/absolute_url" width="23" height="22" alt="InsertTable" title="InsertTable" i18n:attributes="alt ; title" onclick="window.open('epoz_script_table.html','EpozTable','toolbar=no,location=no,status=no,menubar=no,scrollbars=yes,resizable=yes,width=220,height=220')"
+/>&nbsp;<img  tal:attributes="src here/epoz_button_undo.gif/absolute_url" width="23" height="22" alt="Undo" title="Undo" onclick="FormatText('undo', '');" i18n:attributes="alt ; title"
+/><img  tal:attributes="src here/epoz_button_redo.gif/absolute_url" width="23" height="22" alt="Redo" title="Redo" onclick="FormatText('redo', '');" i18n:attributes="alt ; title" />
+</div>  </body>
+
+</html>
\ No newline at end of file
diff --git a/skins/plinn_multilingual.js.pt b/skins/plinn_multilingual.js.pt
new file mode 100644 (file)
index 0000000..bd0d5c7
--- /dev/null
@@ -0,0 +1,17 @@
+var PlinnLang=[];
+<tal:block define="messages python:[
+  'Do you realy want to delete ?'
+, 'confirm_layer_delete'
+, 'Layer '
+, 'New layer'
+, 'Remove layer'
+, 'Hide layer'
+, 'Show layer'
+] ;
+charset request/form/charset;
+dummy python:request.RESPONSE.setHeader('Content-Type', 'text/javascript;;charset=' + charset)
+"
+i18n:domain="plinn"
+>
+<tal:block repeat="m messages">PlinnLang["<tal:block replace="m" />"]="<tal:block content="m" i18n:translate="" tal:omit-tag="" />";</tal:block>
+</tal:block>
\ 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 (file)
index 0000000..855fecc
--- /dev/null
@@ -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 (file)
index 0000000..a2e55b6
--- /dev/null
@@ -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 (file)
index 0000000..5484bcf
--- /dev/null
@@ -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 (file)
index 0000000..ef54386
--- /dev/null
@@ -0,0 +1,69 @@
+<html xmlns="http://www.w3.org/1999/xhtml" metal:use-macro="here/main_template/macros/master" i18n:domain="plinn">
+
+  <head>
+    <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
+               <metal:block metal:fill-slot="javascript_head_slot">
+                       <script type="text/javascript" tal:attributes="src python:'%s/%s?charset=%s' % (portal_url, 'epoz_multilingual.js', here.default_charset)"></script>
+      <script type="text/javascript" tal:attributes="src here/vcXMLRPC.js/absolute_url"></script>
+      <script type="text/javascript" tal:attributes="src here/epoz_script_main.js/absolute_url"></script>
+      <script type="text/javascript" tal:attributes="src here/epoz_redirect.js/absolute_url"></script>
+           <script type="text/javascript" tal:attributes="src here/color_utils.js/absolute_url"></script>
+           <script type="text/javascript" tal:attributes="src string:${portal_url}/plinn_multilingual.js?charset=utf-8"></script>
+           <script type="text/javascript" tal:attributes="src here/layout_controlers.js/absolute_url"></script>
+           <script type="text/javascript" tal:attributes="src here/layout_objects.js/absolute_url"></script>
+           <script type="text/javascript" tal:attributes="src here/xml_io.js/absolute_url"></script>
+               </metal:block>
+  </head>
+
+  <body>
+    <div metal:fill-slot="header">
+      <div metal:use-macro="here/widgets/macros/generic_header">generic_header macro</div>
+    </div>
+    
+<div metal:fill-slot="main" tal:omit-tag="">
+       <!-- tools -->
+       <span id="PlinnDocumentUrl" tal:content="here/absolute_url" style="display:none"></span>
+               <img width="25" height="25" alt="Add text" title="Add text" tal:attributes="src here/plinn_icons/textarea.gif/absolute_url" onclick="new Rectangle(new Point(30, 30), 300, 200, 'EPOZ_ELEMENT').draw(CURRENT_LAYER);" />&nbsp;&nbsp;&nbsp;
+               <input type="hidden" id="ImgDispatcher" value=""
+            onclick="new Rectangle(new Point(30, 30), 200, 200, 'IMG_ELEMENT', 15).draw(CURRENT_LAYER);"/>
+               <input type="hidden" id="EpozImgDispatcher" value=""
+            onclick="CreateImage(PLINN_DOCUMENT_URL+'/'+this.value);">
+    <input type="hidden" id="AttachmentDispatcher" value="" onclick="CreateAttachmentLink(this.value);" />
+               <select id="layerSelector"></select>
+               <img id="layer_status" width="13" height="11" onclick="LAYER_MANAGER.toggleLayerVisibility();"
+                                tal:attributes="src here/plinn_icons/visible_layer.gif/absolute_url"  alt="Toggle layer visibility" title="Hide layer" />
+               
+               <div metal:use-macro="here/plinn_macros/macros/EpozToolbar"></div>
+               
+               <!-- main wysiwyg document node -->
+               <form style="margin:0"><div id="main_space" style="position:relative"></div></form>
+               
+               <!-- elements for cloning -->
+    <div id="elements_pool" style="display:none">
+       <div id="div"></div>
+       <img id="img" alt="image" />
+       <div id="epoz"><iframe src="plinn_blank_iframe.html?charset=utf8&css=zpt_stylesheet.css&customcss=plinn_style.css"
+                                                style="width:100% ; height:100% ; border:none ; background:transparent" allowTransparency="true" frameborder="no"></iframe><textarea style="display:none"></textarea></div>
+    </div>
+    <form action="." style="margin:0" method="post" enctype="multipart/form-data" id="plinn_document_form"
+                       tal:attributes="action string:${here/absolute_url}/plinndocument_edit_form">
+       <input type="hidden" name="onBeforeSubmit" value="_plinnDocumentBeforeSubmit" />
+       <textarea id="xml_output" name="text" style="display:none" rows="1" cols="1"></textarea>
+       <br/><input type="submit" value="Save" name="change_and_view" i18n:attributes="value" />
+    </form>
+    <script type="text/javascript">
+       //<!--
+                       initPlinn(document.getElementById("main_space"), document.getElementById("PlinnDocumentUrl").innerHTML+ "/XMLBody");
+                       var plinnDocumentForm = document.getElementById('plinn_document_form');
+                       if ((AJAX_CONFIG & 2) == 2) {
+                         var fm = new FormManager(plinnDocumentForm);
+                         fm.onBeforeSubmit = _plinnDocumentBeforeSubmit;
+                       }
+                       else {
+                         addListener(plinnDocumentForm, 'submit', _plinnDocumentBeforeSubmit);
+                       }
+                       //-->
+    </script>
+</div>
+  </body>
+</html>
diff --git a/skins/plinndocument_view.pt b/skins/plinndocument_view.pt
new file mode 100644 (file)
index 0000000..c14b64d
--- /dev/null
@@ -0,0 +1,13 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+                        metal:use-macro="here/main_template_view/macros/master">
+
+  <head>
+    <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
+    <title>Plinn document edit</title>
+  </head>
+
+  <body metal:fill-slot="main" tal:omit-tag="">
+    <p tal:replace="structure context/EditableBody"></p>
+  </body>
+
+</html>
\ No newline at end of file
diff --git a/skins/resize_handle.png b/skins/resize_handle.png
new file mode 100644 (file)
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 (file)
index 0000000..bfd7cf0
--- /dev/null
@@ -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 (file)
index 0000000..855fecc
--- /dev/null
@@ -0,0 +1,2 @@
+[default]
+cache=HTTPCache