From 57a4d385a1d2806d5877f53b1fdb0bd94efa2dbb Mon Sep 17 00:00:00 2001
From: =?utf8?q?Beno=C3=AEt=20Pin?= <benoit.pin@gmail.com>
Date: Sun, 7 Nov 2010 03:08:56 +0100
Subject: [PATCH 1/1] Importation initiale.

---
 HistoryAdapters.py                        |  41 +++
 PlinnDocument.py                          | 273 ++++++++++++++++
 __init__.py                               |  35 +++
 configure.zcml                            |  32 ++
 graphics/delete_rectangle.psd             | Bin 0 -> 24724 bytes
 graphics/layer_status.psd                 | Bin 0 -> 21548 bytes
 graphics/resize_handle.psd                | Bin 0 -> 23898 bytes
 interfaces.py                             |   5 +
 license.txt                               | 340 ++++++++++++++++++++
 profiles/default/types.xml                |   4 +
 profiles/default/types/Plinn_Document.xml |  42 +++
 skins/color_utils.js                      | 117 +++++++
 skins/color_utils.js.metadata             |   2 +
 skins/dd_trigger.js                       |  69 ++++
 skins/dd_trigger.js.metadata              |   2 +
 skins/degrad_rectangle_header.gif         | Bin 0 -> 92 bytes
 skins/delete_rectangle.gif                | Bin 0 -> 305 bytes
 skins/delete_rectangle_hover.gif          | Bin 0 -> 304 bytes
 skins/layout_controlers.js                | 365 ++++++++++++++++++++++
 skins/layout_controlers.js.metadata       |   2 +
 skins/layout_objects.js                   | 269 ++++++++++++++++
 skins/layout_objects.js.metadata          |   2 +
 skins/plinn_attachment_form.py            |  28 ++
 skins/plinn_attachment_template.pt        |  56 ++++
 skins/plinn_blank_iframe.html.pt          |  43 +++
 skins/plinn_doc.gif                       | Bin 0 -> 135 bytes
 skins/plinn_doc.thumb.gif                 | Bin 0 -> 1054 bytes
 skins/plinn_file_upload.gif               | Bin 0 -> 386 bytes
 skins/plinn_icons/hidden_layer.gif        | Bin 0 -> 333 bytes
 skins/plinn_icons/imagebox.gif            | Bin 0 -> 301 bytes
 skins/plinn_icons/layer.gif               | Bin 0 -> 257 bytes
 skins/plinn_icons/textarea.gif            | Bin 0 -> 239 bytes
 skins/plinn_icons/visible_layer.gif       | Bin 0 -> 536 bytes
 skins/plinn_image_upload.gif              | Bin 0 -> 1046 bytes
 skins/plinn_init_javascript_code.dtml     |   8 +
 skins/plinn_macros.pt                     |  66 ++++
 skins/plinn_multilingual.js.pt            |  17 +
 skins/plinn_multilingual.js.pt.metadata   |   2 +
 skins/plinndocument_edit_control.py       |  15 +
 skins/plinndocument_edit_form.py          |  13 +
 skins/plinndocument_edit_template.pt      |  69 ++++
 skins/plinndocument_view.pt               |  13 +
 skins/resize_handle.png                   | Bin 0 -> 184 bytes
 skins/xml_io.js                           | 132 ++++++++
 skins/xml_io.js.metadata                  |   2 +
 45 files changed, 2064 insertions(+)
 create mode 100755 HistoryAdapters.py
 create mode 100644 PlinnDocument.py
 create mode 100644 __init__.py
 create mode 100644 configure.zcml
 create mode 100644 graphics/delete_rectangle.psd
 create mode 100644 graphics/layer_status.psd
 create mode 100644 graphics/resize_handle.psd
 create mode 100755 interfaces.py
 create mode 100755 license.txt
 create mode 100644 profiles/default/types.xml
 create mode 100644 profiles/default/types/Plinn_Document.xml
 create mode 100644 skins/color_utils.js
 create mode 100644 skins/color_utils.js.metadata
 create mode 100644 skins/dd_trigger.js
 create mode 100644 skins/dd_trigger.js.metadata
 create mode 100644 skins/degrad_rectangle_header.gif
 create mode 100644 skins/delete_rectangle.gif
 create mode 100644 skins/delete_rectangle_hover.gif
 create mode 100644 skins/layout_controlers.js
 create mode 100644 skins/layout_controlers.js.metadata
 create mode 100644 skins/layout_objects.js
 create mode 100644 skins/layout_objects.js.metadata
 create mode 100644 skins/plinn_attachment_form.py
 create mode 100644 skins/plinn_attachment_template.pt
 create mode 100755 skins/plinn_blank_iframe.html.pt
 create mode 100644 skins/plinn_doc.gif
 create mode 100644 skins/plinn_doc.thumb.gif
 create mode 100644 skins/plinn_file_upload.gif
 create mode 100644 skins/plinn_icons/hidden_layer.gif
 create mode 100644 skins/plinn_icons/imagebox.gif
 create mode 100644 skins/plinn_icons/layer.gif
 create mode 100644 skins/plinn_icons/textarea.gif
 create mode 100644 skins/plinn_icons/visible_layer.gif
 create mode 100644 skins/plinn_image_upload.gif
 create mode 100644 skins/plinn_init_javascript_code.dtml
 create mode 100644 skins/plinn_macros.pt
 create mode 100644 skins/plinn_multilingual.js.pt
 create mode 100644 skins/plinn_multilingual.js.pt.metadata
 create mode 100644 skins/plinndocument_edit_control.py
 create mode 100644 skins/plinndocument_edit_form.py
 create mode 100644 skins/plinndocument_edit_template.pt
 create mode 100644 skins/plinndocument_view.pt
 create mode 100644 skins/resize_handle.png
 create mode 100644 skins/xml_io.js
 create mode 100644 skins/xml_io.js.metadata

diff --git a/HistoryAdapters.py b/HistoryAdapters.py
new file mode 100755
index 0000000..add0e8b
--- /dev/null
+++ b/HistoryAdapters.py
@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+#######################################################################################
+#   Plinn - http://plinn.org                                                          #
+#   Copyright © 2005-2009  Benoît PIN <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
index 0000000..3267069
--- /dev/null
+++ b/PlinnDocument.py
@@ -0,0 +1,273 @@
+# -*- coding: utf-8 -*-
+#######################################################################################
+#   Plinn - http://plinn.org                                                          #
+#   Copyright (C) 2005-2007  Benoît PIN <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
index 0000000..a2dc2a1
--- /dev/null
+++ b/__init__.py
@@ -0,0 +1,35 @@
+# -*- coding: utf-8 -*-
+#######################################################################################
+#   Plinn - http://plinn.org                                                          #
+#   Copyright (C) 2005-2007  Benoît PIN <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
index 0000000..e72308a
--- /dev/null
+++ b/configure.zcml
@@ -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
index 0000000000000000000000000000000000000000..8a6823c8b40188de90c889682e5842a37a50ac50
GIT binary patch
literal 24724
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

literal 0
HcmV?d00001

diff --git a/graphics/layer_status.psd b/graphics/layer_status.psd
new file mode 100644
index 0000000000000000000000000000000000000000..5725cb700aa1f2e2fe0364d89390890818fb7a6e
GIT binary patch
literal 21548
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
YIe?=G7=;5uBe9P>Ae%*7HpnpfzvMI#K>z>%

literal 0
HcmV?d00001

diff --git a/graphics/resize_handle.psd b/graphics/resize_handle.psd
new file mode 100644
index 0000000000000000000000000000000000000000..2baf5c073d21cb754249d74ef06f69be560be8b9
GIT binary patch
literal 23898
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

literal 0
HcmV?d00001

diff --git a/interfaces.py b/interfaces.py
new file mode 100755
index 0000000..ca0edbd
--- /dev/null
+++ b/interfaces.py
@@ -0,0 +1,5 @@
+from zope.interface import Interface
+
+class IPlinnDocument(Interface):
+	""" Plinn document
+	"""
diff --git a/license.txt b/license.txt
new file mode 100755
index 0000000..3912109
--- /dev/null
+++ b/license.txt
@@ -0,0 +1,340 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <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
index 0000000..04e5f83
--- /dev/null
+++ b/profiles/default/types.xml
@@ -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
index 0000000..bce5392
--- /dev/null
+++ b/profiles/default/types/Plinn_Document.xml
@@ -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
index 0000000..7c70d07
--- /dev/null
+++ b/skins/color_utils.js
@@ -0,0 +1,117 @@
+// (c) Benoît PIN 2006-2007
+// http://plinn.org
+// Licence GPL
+
+function HSVColor(h, s, v) {
+	this.h = h; // 0-360°
+	this.s = s; // 0-100%
+	this.v = v; // 0-100%
+}
+
+HSVColor.prototype.toRGBString = function() {
+	var rgb = this.toRGB();
+	return "rgb(" + String(rgb[0]) + ", " + String(rgb[1]) + ", " + String(rgb[2]) + ")";
+};
+
+HSVColor.prototype.toRGB = function () {
+	var h = this.h;	var s = this.s/100.0;
+	var v = this.v/100.0;
+	if( s == 0 )		 				// achromatic (grey)
+		r = g = b = v;
+
+	h = h/60;							// sector 0 to 5
+	var i = Math.floor( h );
+	var f = h - i;						// factorial part of h
+	var p = v * ( 1 - s );
+	var q = v * ( 1 - s * f );
+	var t = v * ( 1 - s * ( 1 - f ) );
+	switch( i ) {
+		case 0:
+			r = v;
+			g = t;
+			b = p;
+			break;
+		case 1:
+			r = q;
+			g = v;
+			b = p;
+			break;
+		case 2:
+			r = p;
+			g = v;
+			b = t;
+			break;
+		case 3:
+			r = p;
+			g = q;
+			b = v;
+			break;
+		case 4:
+			r = t;
+			g = p;
+			b = v;
+			break;
+		default:				// case 5:
+			r = v;
+			g = p;
+			b = q;
+			break;
+	}
+	
+	r = Math.round(r*255);
+	g = Math.round(g*255);
+	b = Math.round(b*255);
+	return [r, g, b];
+};
+
+
+function ColorIterator(baseH, baseS, baseV, step) {
+	this.baseColor = new HSVColor(baseH, baseS, baseV);
+	this.currentH = baseH;
+	this.step = step;
+}
+
+ColorIterator.prototype.next = function() {
+	var c = new HSVColor(this.currentH, this.baseColor.s, this.baseColor.v);
+	this.currentH += this.step;
+
+	if (this.currentH >= 360)
+		this.currentH -= 360;
+
+	return c;
+};
+
+function setBorderColor(ob, colorString) {
+	var s = ob.style;
+	s.borderBottomColor = colorString;
+	s.borderLeftColor = colorString;
+	s.borderRightColor = colorString;
+	s.borderTopColor = colorString;
+}
+
+
+function setBorderStyle(ob, borderStyle) {
+	var s= ob.style;
+	s.borderBottomStyle = borderStyle;
+	s.borderLeftStyle = borderStyle;
+	s.borderRightStyle = borderStyle;
+	s.borderTopStyle = borderStyle;
+}
+
+
+function setBorderWidth(ob, borderWidth) {
+	var s= ob.style;
+	s.borderBottomWidth = borderWidth;
+	s.borderLeftWidth = borderWidth;
+	s.borderRightWidth = borderWidth;
+	s.borderTopWidth = borderWidth;
+}
+
+function zfill(number, digits) {
+	number = Math.floor(number);
+	var nStr = String(number);
+	while (nStr.length < digits)
+		nStr = "0" + nStr;
+	return nStr;
+}
+
diff --git a/skins/color_utils.js.metadata b/skins/color_utils.js.metadata
new file mode 100644
index 0000000..855fecc
--- /dev/null
+++ b/skins/color_utils.js.metadata
@@ -0,0 +1,2 @@
+[default]
+cache=HTTPCache
diff --git a/skins/dd_trigger.js b/skins/dd_trigger.js
new file mode 100644
index 0000000..b79f37e
--- /dev/null
+++ b/skins/dd_trigger.js
@@ -0,0 +1,69 @@
+// (c) Benoît PIN 2006-2007
+// http://plinn.org
+// Licence GPL
+
+function ddEventDispatch() {
+	addListener(document, 'mousedown', raisemousedown);
+	addListener(document, 'mousemove', raisemousemove);
+	addListener(document, 'mouseup', raisemouseup);
+}
+
+
+function getRectangleNode() {
+	var docid = document.id;
+	docid = document.id.slice(DocPrefixLength);
+	var frameId = IFramePrefix + docid;
+	iframe = window.parent.document.getElementById(frameId);
+	if (!iframe)
+		iframe = window.parent.document.getElementById("EpozEditor");
+	return iframe.parentNode;
+}
+
+var raisemousedown, raisemousemove, raisemouseup;
+
+if (browser.isIE55 || browser.isIE6up) {
+	raisemousedown = function() {
+		var rect = getRectangleNode();
+		rect.fireEvent('onmousedown', window.event);
+	};
+
+	raisemousemove = function() {
+		var rect = getRectangleNode();
+		rect.fireEvent('onmousemove', window.event);
+		rect.fireEvent('onmouseover', window.event);
+	};
+
+	raisemouseup = function() {
+		var rect = getRectangleNode();
+		rect.fireEvent('onmouseup', window.event);
+	};
+}
+else if (browser.isDOM2Event) {
+	// initMouseEvent("eventType", bubblesFlas, cancelableFlag, view, detailVal, screenX, screenY, clientX, clientY, ctrlKeyFlag, altKeyFlag, shiftKeyFlag, metaKeyFlag, buttonCode, relatedTargetNodeRef)
+
+	raisemousedown = function(e) {
+		var event = document.createEvent("MouseEvents");
+		var rect = getRectangleNode()
+		event.initMouseEvent("mousedown", true, true, window.parent, e.detail, e.screenX, e.screenY, e.clientX, e.clientY, e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, e.button, rect);
+		rect.dispatchEvent(event);
+	};
+
+
+	raisemousemove = function(e) {
+		var overEvent = document.createEvent("MouseEvents");
+		overEvent.initMouseEvent("mouseover", true, true, window.parent, e.detail, e.screenX, e.screenY, e.clientX, e.clientY, e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, e.button, rect);
+		var event = document.createEvent("MouseEvents");
+		var rect = getRectangleNode()
+		event.initMouseEvent("mousemove", true, true, window.parent, e.detail, e.screenX, e.screenY, e.clientX, e.clientY, e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, e.button, rect);
+		rect.dispatchEvent(event);
+		rect.dispatchEvent(overEvent);
+	};
+
+
+	raisemouseup = function(e) {
+		var event = document.createEvent("MouseEvents");
+		var rect = getRectangleNode()
+		event.initMouseEvent("mouseup", true, true, window.parent, e.detail, e.screenX, e.screenY, e.clientX, e.clientY, e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, e.button, rect);
+		rect.dispatchEvent(event);
+	};
+}
diff --git a/skins/dd_trigger.js.metadata b/skins/dd_trigger.js.metadata
new file mode 100644
index 0000000..855fecc
--- /dev/null
+++ b/skins/dd_trigger.js.metadata
@@ -0,0 +1,2 @@
+[default]
+cache=HTTPCache
diff --git a/skins/degrad_rectangle_header.gif b/skins/degrad_rectangle_header.gif
new file mode 100644
index 0000000000000000000000000000000000000000..721dc3571638bff4cda185476305264b4f08e92f
GIT binary patch
literal 92
zcmZ?wbhEHbWMtrG*v!E2`0=9$5AMHw_2Sme8?Ro!eE#Cu)2B~fzj^ij`}aF{Zr{9l
fg8>yN{$v3Q=zvI&84N6(0<ub7jEXA-85yhrL%Sm_

literal 0
HcmV?d00001

diff --git a/skins/delete_rectangle.gif b/skins/delete_rectangle.gif
new file mode 100644
index 0000000000000000000000000000000000000000..e70f5a9bd0569c485f3ff080be118dd5b16e66d3
GIT binary patch
literal 305
zcmZ?wbhEHb<Y3@txXQqA;r#ig=BAd`mK7_O&z?Pd+qP|I&YZb>_s*)-tJ>Py_V3&O
z^5ygIKfX_&KK<dt`+N89ef|37ix<zYT)uquDv-D|d-kl3j`rotm%V=T>gCH9tAV=O
z+x7v$-n|bWK4@=mI{^g8k2f_nZQr)-?3puHu3Uce=JnmXw`WYB{_Xp>*4CEKpFiEY
zb#uXj`4cAe-@A8r#mW_FX(?w;pMLu6$;($SzJLGr`t_^t-@o6wdE@5I8~^|RXFvst
zKUu&6Iv^6{Ck8e@hhuNUR^JUW?RhKI&FizxmFd}s17?D~TnCaESvn4Qgv~m^wWwcZ
vbJRtRX>CsK*9!yKoY<49Lp<0Ob@lXg71S;CjZI99^(`$eCQi0+WUvMRp*^Rj

literal 0
HcmV?d00001

diff --git a/skins/delete_rectangle_hover.gif b/skins/delete_rectangle_hover.gif
new file mode 100644
index 0000000000000000000000000000000000000000..fba2ab657c7e8ed46c15983441b3208ffb827b4a
GIT binary patch
literal 304
zcmV-00nh$NNk%w1VF&;V0M!5hZag}KYiD~#L)zNegllNXnwW)zgP@_HgmQ7NcW8uh
zaLLHX@4~m~>geg|=-H{8-rn8c-`%r>gRH8m#$;0K?CPyVIqdA}iBL_(Wm0ZCI@ze2
zdqqR7c4+ta_n@GlkBf_|si|5mEX|gWr>Lj*`1jo0+oq?c$jHdu-Q13gimXF9@4~i3
z7#4<vgQ1p|-q_e>Wn-F-joR7RmywX@=;!S0>%Lt{<>utf%*%U5L`4}F_xJYM+1LO8
z00000A^8LV00000EC2ui00;mJ000I}K%P+ODe)?{Q!GZZU23iu4E8eACYs$yumgk?
ztJ_X=vmgSQolerC5NI3>i&4<ga<a~2laS?1C<h=a9}6in5I7AEI1vLqkUcYzK>#}u
Cq?f+{

literal 0
HcmV?d00001

diff --git a/skins/layout_controlers.js b/skins/layout_controlers.js
new file mode 100644
index 0000000..c0a383e
--- /dev/null
+++ b/skins/layout_controlers.js
@@ -0,0 +1,365 @@
+// (c) Benoît PIN 2006-2007
+// http://plinn.org
+// Licence GPL
+
+// Global variables
+var CURRENT_LAYER;
+var LAYER_MANAGER;
+var XML_OUTPUT;
+var PLINN_DOCUMENT_URL;
+var GLOBAL_DD_CONTROLER;
+
+function initPlinn(root_container, xmlDataUrl) {
+	PLINN_DOCUMENT_URL = document.getElementById("PlinnDocumentUrl").innerHTML;
+	form_path = PLINN_DOCUMENT_URL+'/'; // for epoz
+	initElementsPool();
+	initWidgets();
+	var imp = new XMLImport(xmlDataUrl, root_container);
+	GLOBAL_DD_CONTROLER = new GlobalPlinnDDControler(root_container);
+}
+
+
+function initWidgets() {
+	// Global control widgets
+	XML_OUTPUT = document.getElementById("xml_output")
+}
+
+function initLayerManager(root_container) {
+	LAYER_MANAGER = new LayerManager(root_container, new ColorIterator(240, 80, 60, 83)); // todo : move in config file
+}
+
+function Point(x, y) {
+	this.x = x;
+	this.y = y;
+}
+Point.prototype.difference = function(point) { return new Point(this.x - point.x, this.y - point.y); };
+Point.prototype.addition = function(point) { return new Point(this.x + point.x, this.y + point.y); };
+Point.prototype.toString = function() { return "(" + String(this.x) + ", " + String(this.y) + ")"; };
+
+
+function GlobalPlinnDDControler(root_container) {
+	this.root_container = root_container;
+	this.dragInProgress = false;
+	
+	this.currentRect = null;
+	this.dragAllowed	= null;
+	this.resizeAllowed	= null;
+	this.scalable = null;
+	this.deleteAllowed = null;
+	
+
+	//Constants
+	this.DRAG_MODE = 0;
+	this.RESIZE_MODE = 1;
+	
+	this.mode = this.DRAG_MODE;
+	this.ddEventCaptureElmt = document;
+	
+	// event listeners
+	var thisControler = this;
+	with (this.root_container) {
+		onmouseover	= function(evt){thisControler.onmouseover(evt);};
+		onmouseout	= function(evt){thisControler.onmouseout(evt);};
+	}
+	with (this.ddEventCaptureElmt) {
+		onmousedown	= function(evt){thisControler.onmousedown(evt);};
+		onmousemove	= function(evt){thisControler.onmousemove(evt);};
+		onmouseup	= function(evt){thisControler.onmouseup(evt);};
+	}
+}
+
+GlobalPlinnDDControler.prototype.onmouseover = function(evt) {
+	if (this.dragInProgress) return;
+	var ob = getTargetedObject(evt);
+	var rect = ob.rectangle;
+	if (!rect) return;
+	rect.showHandles();
+};
+
+GlobalPlinnDDControler.prototype.onmouseout = function(evt) {
+	if (this.dragInProgress) return;
+	var ob = getTargetedObject(evt);
+	var rect = ob.rectangle;
+	if (!rect) return;
+	rect.hideHandles();
+};
+
+GlobalPlinnDDControler.prototype.onmousedown = function(evt) {
+	var ob = getTargetedObject(evt);
+	var rect = ob.rectangle;
+
+	if (!rect) return;
+
+	this.currentRect = rect;
+	this.setDDOptions(rect.ddOptions);
+	
+	var evt = getEventObject(evt);
+	if (this.deleteAllowed && (
+		(evt.shiftKey && evt.altKey) ||
+		(ob.tagName == "IMG" && ob.className=="rectangle_delimg")) ) {
+		if (confirm(PlinnLang["Do you realy want to delete ?"])) {
+			var hostDiv = rect.hostDiv;
+			hostDiv.parentNode.removeChild(hostDiv);
+		}
+		return;
+	}
+	
+	this.originalClickPoint = new Point(evt.screenX, evt.screenY);
+	this.originalSize = new Point(rect.width, rect.height);
+	this.offsetPoint = rect.upperLeftCorner.difference(this.originalClickPoint);
+
+	if (evt.shiftKey || (ob.tagName == "SPAN" && ob.className=="resize_handle")) {
+		this.mode = this.RESIZE_MODE;
+		this.startDrag(evt, rect);
+	}
+	else if(evt.altKey || (ob.tagName == "DIV" && ob.className=="rectangle_header")){
+		this.mode = this.DRAG_MODE;
+		this.startDrag(evt, rect);
+	}
+};
+
+GlobalPlinnDDControler.prototype.onmousemove = function(evt) {
+	var evt = getEventObject(evt);
+	if (this.dragInProgress) {
+		var rectangle = this.currentRect;
+		if (this.dragAllowed && (this.mode == this.DRAG_MODE)) {
+			var relativeEvtPoint = new Point(evt.screenX, evt.screenY).addition(this.offsetPoint);
+			rectangle.moveTo(relativeEvtPoint);
+		}
+		else if (this.resizeAllowed && (this.mode == this.RESIZE_MODE)) {
+			var delta = new Point(evt.screenX, evt.screenY).difference(this.originalClickPoint);
+			var size = this.originalSize.addition(delta);
+			if (this.scalable) {
+				size = new Point(size.x, Math.round(size.x * rectangle.ratio));
+			}
+
+			rectangle.resizeTo(size);
+		}
+		if (browser.isIE) {
+			try {document.selection.clear();}
+			catch (e){return;}
+		}
+	}
+};
+
+GlobalPlinnDDControler.prototype.onmouseup = function(evt) {
+	this.drop()
+	var rectangle = this.currentRect;
+	if (rectangle && rectangle.ondrop)
+		rectangle.ondrop();
+};
+
+
+GlobalPlinnDDControler.prototype.startDrag = function(evt, rect) {
+	disablePropagation(evt);
+	disableDefault(evt);
+	this.dragInProgress = true;
+};
+
+GlobalPlinnDDControler.prototype.drop = function() {this.dragInProgress = false;};
+
+GlobalPlinnDDControler.prototype.setDDOptions = function(options){
+	this.dragAllowed	= (options & 1) == 1;
+	this.resizeAllowed	= (options & 2) == 2;
+	this.scalable 		= (options & 4) == 4;
+	this.deleteAllowed 	= (options & 8) == 8;
+};
+
+
+
+function LayerManager(main_space, colorIterator) {
+	this.space = main_space;
+	this.layers = new Array();
+	this.defaultLayerWidth = 800;
+	this.defaultLayerHeight = 600;
+	this.colorIterator = colorIterator;
+	this.layerSelector = document.getElementById('layerSelector');
+	var thisManager = this;
+
+	// add layer selector commands
+	// separator between layers list and layer commandes
+	var sepOpt = document.createElement("option");
+	sepOpt.innerHTML = "--------";
+	sepOpt.disabled = "disabled";
+	sepOpt.style.color = "#888";
+	
+	// new layer command
+	var addLayerOpt = document.createElement("option");
+	addLayerOpt.value = "add";
+	addLayerOpt.innerHTML = PlinnLang["New layer"];
+	addLayerOpt.style.color = "#000";
+	
+	// remove layer command
+	var removeLayerOpt = document.createElement("option");
+	removeLayerOpt.value = "rm";
+	removeLayerOpt.innerHTML = PlinnLang["Remove layer"];
+	removeLayerOpt.disabled = "disabled";
+	
+	with (this.layerSelector) {
+		appendChild(sepOpt);
+		appendChild(addLayerOpt);
+		appendChild(removeLayerOpt);
+	}
+	addListener(this.layerSelector, 'change', function(){thisManager.activateLayer();});
+}
+
+LayerManager.prototype.addLayer = function(position) {
+	var color = this.colorIterator.next();
+	var mode = 6; // resize and delete
+	if (this.layers.length <= 1)
+		mode = 2; // resize only
+	var l = new Rectangle(new Point(0, 0), this.defaultLayerWidth, this.defaultLayerHeight, "DIV_ELEMENT", 2);
+	setBorderColor(l, color.toRGBString());
+	l.onresize = resizeOnMouseMove;
+	l.ondrop = resizeOnDrop;
+	l.style.overflow = "hidden";
+	l.draw(this.space, position);
+	var index = this.layers.push(l) - 1;
+	
+	var opt = document.createElement("option");
+	strId = String(index);
+	opt.value = strId;
+	opt.id = "layer_" + strId;
+	opt.innerHTML = PlinnLang["Layer "] + strId;
+	opt.style.color = color.toRGBString();
+	this.layerSelector.insertBefore(opt, this.layerSelector.childNodes[this.layers.length-1]);
+	
+	// If there's only one layer, the remove command is disabled.
+	if (this.layers.length > 1) {
+		var rmCommand = this.layerSelector.childNodes[this.layerSelector.length -1]
+		rmCommand.removeAttribute("disabled");
+		rmCommand.style.color="#000";
+	}
+	this.activateLayer(index);
+};
+	
+LayerManager.prototype.activateLayer = function(index) {
+	var layerSelector = this.layerSelector;
+	var index = (index != null) ? index : layerSelector.value;
+	// handle commands
+	index = ( Number(index) || Number(index ) == 0 ) ? Number(index) : index
+
+	if (typeof(index) == typeof('')) {
+		switch (index) {
+			case "add" :
+				this.addLayer();
+				break;
+			case "rm" :
+				this.removeCurrentLayer();
+				break;
+		};
+		return;
+	}
+	
+	CURRENT_LAYER = this.layers[index];
+	this._syncLayerStatusImage();
+	var opt = document.getElementById("layer_" + String(index));
+	layerSelector.selectedIndex = opt.index;
+	layerSelector.style.color = opt.style.color;
+	this.putCurrentLayerOnTop();
+};
+
+
+LayerManager.prototype.putCurrentLayerOnTop = function() {
+	for(var i = 0 ; i < this.layers.length ; i++) {
+		layer = this.layers[i];
+		if (layer == CURRENT_LAYER)
+			layer.style.zIndex = String(this.layers.length);
+		else
+			layer.style.zIndex = String(i);
+	}
+};
+	
+LayerManager.prototype.toggleLayerVisibility = function(){
+	var currentVisibility = CURRENT_LAYER.style.visibility;
+	if (currentVisibility == "hidden")
+		CURRENT_LAYER.style.visibility = "visible";
+	else
+		CURRENT_LAYER.style.visibility = "hidden";
+
+	this._syncLayerStatusImage();
+};
+
+LayerManager.prototype._syncLayerStatusImage = function() {
+	var layerStatusImage = document.getElementById("layer_status");
+	if (CURRENT_LAYER.style.visibility == "hidden") {
+		layerStatusImage.src = "plinn_icons/hidden_layer.gif";
+		layerStatusImage.title = PlinnLang["Show layer"];
+	}
+	else {
+		layerStatusImage.src = "plinn_icons/visible_layer.gif";
+		layerStatusImage.title = PlinnLang["Hide layer"];
+	}
+};
+	
+LayerManager.prototype.removeCurrentLayer = function() {
+	var layersLength = this.layers.length;
+	var layerSelector = this.layerSelector;
+	var layer, deletedLayerIndex;
+	for (var i = 0 ; i < layersLength ; i++) {
+		layer = this.layers[i];
+		if (layer == CURRENT_LAYER) {
+			if (confirm(PlinnLang["confirm_layer_delete"] + i + "\" ?")) {
+				this.space.removeChild(layer.hostDiv);
+				this.layers.splice(i, 1);
+				layerSelector.removeChild(layerSelector.childNodes[i]);
+				deletedLayerIndex = i;
+				break;
+			}
+			else {
+				this.activateLayer(i);
+				return;
+			}
+		}
+	}
+	
+	// update layer indexes
+	for (var i = 0 ; i < this.layers.length ; i++) {
+		layerSelector.childNodes[i].value = String(i);
+		layerSelector.childNodes[i].id = "layer_" + String(i);
+   		layerSelector.childNodes[i].innerHTML = PlinnLang["Layer "] + String(i);
+	}
+
+	if (this.layers.length == 1) {
+		var removeCmdOpt = layerSelector.childNodes[layerSelector.childNodes.length - 1];
+		removeCmdOpt.disabled = "disabled";
+		removeCmdOpt.style.color = "#888";
+	}
+
+	if (deletedLayerIndex == 0)
+		this.layers[0].style.position = "relative";
+
+	// activate previous layer
+	this.activateLayer((deletedLayerIndex > 0) ? --deletedLayerIndex : 0);
+	
+};
+
+function resizeOnMouseMove() {
+	if (LAYER_MANAGER.layers.length > 5)
+		return; // to expensive
+	else
+		_resizeAllLayers(this);
+}
+
+function resizeOnDrop() { _resizeAllLayers(this);}
+
+function _resizeAllLayers(layer) {
+	var layers = LAYER_MANAGER.layers;
+	var size = new Point(layer.width, layer.height);
+	var l;
+	for(var i = 0 ; i < layers.length ; i++) {
+		l = layers[i];
+		if (layer != l)
+			l.resizeTo(size, true);
+	}
+	LAYER_MANAGER.defaultLayerWidth = size.x;
+	LAYER_MANAGER.defaultLayerHeight = size.y;
+}
+
+function CreateAttachmentLink(data) {
+	// data like that : "title[attachment/a_file]
+	var title = data.split("\[", 1)[0];
+	var url =data.match(/\[(.*)\]/)[1];
+	var link ="<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
index 0000000..855fecc
--- /dev/null
+++ b/skins/layout_controlers.js.metadata
@@ -0,0 +1,2 @@
+[default]
+cache=HTTPCache
diff --git a/skins/layout_objects.js b/skins/layout_objects.js
new file mode 100644
index 0000000..2d4ca89
--- /dev/null
+++ b/skins/layout_objects.js
@@ -0,0 +1,269 @@
+// (c) Benoît PIN 2006-2007
+// http://plinn.org
+// Licence GPL
+
+var ELEMENTS_POOL = {};
+
+function initElementsPool() {
+	// Element pool (for cloning)
+	ELEMENTS_POOL["DIV_ELEMENT"]	= {"node" : document.getElementById("div"),
+									   "beforeDraw" : null,
+									   "getRawData" : null,
+									   "putData" : null };
+
+	imgBeforeDraw = function() {
+		if (!this.node.src) {
+			var dispatch = document.getElementById("ImgDispatcher");
+			var strRatio = dispatch.value.split("_", 1)[0];
+			var src = dispatch.value.substring(strRatio.length+1);
+			this.ratio = new Number(strRatio).valueOf();
+			this.node.src = PLINN_DOCUMENT_URL + '/' + src;
+			var w = this.width;
+			var size = (this.ratio > 1) ?
+				new Point(w, Math.round(w * this.ratio)) :
+				new Point(Math.round(w / this.ratio), w);
+			
+			this.resizeTo(size);
+		}
+	}
+
+	ELEMENTS_POOL["IMG_ELEMENT"]	= {"node" : document.getElementById("img"),
+									   "beforeDraw" : imgBeforeDraw,
+									   "getRawData" : function() {
+									   		var src = this.node.src;
+									   		src = src.split('/');
+									   		return "attachments/" + src[src.length - 2] + "/getThumbnail";
+									   		},
+									   "putData" : function(raw) {
+									   		this.node.src = PLINN_DOCUMENT_URL+"/attachments/"+raw;
+									   		}
+									    };
+
+	epozBeforeDraw = function() {
+		var rect = this
+		var iframe = this.node.childNodes[0];
+		var name = String(Math.floor(Math.random() * 10000));
+		addListener(iframe, 'load', function(){initTextRectangle(iframe, name, rect);});
+		
+		var ta = this.node.childNodes[1];
+		ta.name = name;
+		ta.id = name;
+	}
+
+	ELEMENTS_POOL["EPOZ_ELEMENT"]	= {"node" : document.getElementById("epoz"),
+									   "beforeDraw" : epozBeforeDraw,
+									   "getRawData" : function() {
+									   		return this.node.childNodes[0].contentWindow.document.body.innerHTML;
+									   		},
+									   "putData" : function(raw) {
+									   		this.node.childNodes[1].value = raw;
+									   		}
+									   	};
+
+	for (var i = 0 ; i < ELEMENTS_POOL.length ; i++)
+		ELEMENTS_POOL[i].removeAttribute("id");
+}
+
+function Rectangle(upperLeftCorner, width, height, elementKey, ddOptions, ratio) {
+	this.meta_type = "Rectangle"; // ;-)
+	this.upperLeftCorner = upperLeftCorner;
+	this.width = width;
+	this.height = height;
+	this.elementKey = elementKey;
+	this.node = ELEMENTS_POOL[elementKey]["node"].cloneNode(true);
+	with (this.node.style) {
+		width="100%";
+		height="100%";
+	}
+	this.beforeDraw = ELEMENTS_POOL[elementKey]["beforeDraw"];
+	this.getRawData = ELEMENTS_POOL[elementKey]["getRawData"];
+	this.ddOptions = (!ddOptions) ? 11 : ddOptions; // 11-> drag, resize, delete
+	this.ratio = ratio;
+	
+	this.hostDiv = document.createElement("div");
+	this.hostDiv.appendChild(this.node);
+	
+	this.resizeHandle = document.createElement("span");
+	this.resizeHandle.className = "resize_handle";
+	this.hostDiv.appendChild(this.resizeHandle);
+	
+	if((this.ddOptions & 8) == 8) { // move allowed
+		this.movDelHandle = document.createElement("div");
+		this.movDelHandle.className = "rectangle_header";
+		var delImg = document.createElement("img");
+		delImg.src = "delete_rectangle.gif";
+		delImg.className = "rectangle_delimg";
+		delImg.onmouseover=function(){this.src="delete_rectangle_hover.gif";}
+		delImg.onmouseout=function(){this.src="delete_rectangle.gif";}
+		this.movDelHandle.appendChild(delImg);
+		this.hostDiv.appendChild(this.movDelHandle);
+		this.movDelHandle.rectangle = this;
+		delImg.rectangle = this;
+		
+	}
+	
+	// backward references
+	this.style = this.hostDiv.style;
+	this.node.rectangle = this;
+	this.hostDiv.rectangle = this;
+	this.resizeHandle.rectangle = this;
+}
+
+Rectangle.prototype.showHandles = function() {
+	if (this.resizeHandle)
+		this.resizeHandle.style.visibility = "visible";
+	if (this.movDelHandle)
+		this.movDelHandle.style.visibility = "visible";
+};
+
+Rectangle.prototype.hideHandles = function() {
+	if (this.resizeHandle)
+		this.resizeHandle.style.visibility = "hidden";
+	if (this.movDelHandle)
+		this.movDelHandle.style.visibility = "hidden";
+};
+
+Rectangle.prototype._mac_moveDelHandle = function() {
+	// fix height
+	var epozWindow = this.node.childNodes[0].contentWindow;
+	if (epozWindow.scrollMaxY)
+		this.resizeHandle.style.right = "15px";
+	else
+		this.resizeHandle.style.right = "0px";
+
+	// fix width
+	if (epozWindow.scrollMaxX)
+		this.resizeHandle.style.bottom = "15px";
+	else
+		this.resizeHandle.style.bottom = "0px";
+};
+	
+
+Rectangle.prototype.draw = function(container, position) {
+	if (this.beforeDraw)
+		this.beforeDraw();
+	var style = this.style;
+	style.left = String(this.upperLeftCorner.x) + "px"; 
+	style.top = String(this.upperLeftCorner.y) + "px";
+	style.width = String(this.width) + "px";
+	style.height = String(this.height) + "px";
+	
+	style.display="block";
+	var pos;
+	if (!position)
+		pos = "absolute";
+	else
+		pos = position;
+	style.position = pos;
+	setBorderStyle(this, "solid");
+	setBorderWidth(this, "1px");
+	style.visibility = "inherit";
+/*	style.overflow = "hidden";*/
+	if (container.meta_type == "Rectangle")
+		setBorderColor(this, container.style.borderTopColor);
+	
+	container.appendChild(this.hostDiv);
+};
+
+
+Rectangle.prototype.moveTo = function(upperLeftCorner) {
+	this.upperLeftCorner = upperLeftCorner;
+	var style = this.style;
+	style.left = String(this.upperLeftCorner.x) + "px"; 
+	style.top = String(this.upperLeftCorner.y) + "px";
+};
+
+Rectangle.prototype.resizeTo = function(size, cancelOnresize) {
+	if (size.x >= 10)
+		this.width = size.x;
+	if (size.y >= 10)
+		this.height = size.y;
+		
+	var style = this.style;
+	style.width = String(this.width) + "px";
+	style.height = String(this.height) + "px";
+	if (this.onresize && !cancelOnresize)
+		this.onresize();
+};
+
+Rectangle.prototype.appendChild = function(node) {
+	this.node.appendChild(node);
+};
+
+Rectangle.prototype.setId = function(id) {
+	this.node.id = id;
+};
+
+
+
+function initTextRectangle(iframe, name, rect) {
+	var ta = iframe.nextSibling; // ta -> textarea
+	var data = ta.value;
+	
+	if (browser.isGecko) {
+		// Just a few cleanups for Mozilla
+		data = data.replace(/<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
index 0000000..855fecc
--- /dev/null
+++ b/skins/layout_objects.js.metadata
@@ -0,0 +1,2 @@
+[default]
+cache=HTTPCache
diff --git a/skins/plinn_attachment_form.py b/skins/plinn_attachment_form.py
new file mode 100644
index 0000000..090c57b
--- /dev/null
+++ b/skins/plinn_attachment_form.py
@@ -0,0 +1,28 @@
+##parameters=
+form = context.REQUEST.form
+formId = form.get('formId')
+epozInsert = form.get('epozInsert', False)
+file = form.get('file', None)
+fileOb = None
+
+if file : fileOb = context.addAttachment(file, formId)
+
+onload = None
+if fileOb is not None :
+	if fileOb.meta_type == 'Photo' :
+		fileUrl = 'attachments/%s/getThumbnail' % fileOb.getId()
+		if epozInsert :
+			onload = "dispatchImageAndHideUploadForm('%s', '%s', true);" % (fileUrl, formId)
+		else :
+			ratio = float(fileOb.height)/fileOb.width
+			dispatchValue = "%f_%s" % (ratio, fileUrl)
+			onload = "dispatchImageAndHideUploadForm('%s','%s', false);" % (dispatchValue, formId)
+	else :
+		dispatchValue = '%s[%s]' % (fileOb.getId(), 'attachments/'+fileOb.getId())
+		onload = "dispatchFileAndHideUploadForm('%s', '%s');" % (dispatchValue, formId)
+
+hideFormCode = "hideUploadForm('%s');" % formId
+
+return context.plinn_attachment_template(onload=onload,
+										 hideFormCode=hideFormCode,
+										 formId=formId)
\ No newline at end of file
diff --git a/skins/plinn_attachment_template.pt b/skins/plinn_attachment_template.pt
new file mode 100644
index 0000000..062bfdc
--- /dev/null
+++ b/skins/plinn_attachment_template.pt
@@ -0,0 +1,56 @@
+<!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
index 0000000..7029a91
--- /dev/null
+++ b/skins/plinn_blank_iframe.html.pt
@@ -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
index 0000000000000000000000000000000000000000..a92c9aec8c5c990ebad6df4a6c85d9def42a5fa0
GIT binary patch
literal 135
zcmV;20C@jLNk%w1VGsZi0FeU#V`_28*W~~J0RR90A^sRVLr+jyK0;+~Z|-y;P)SZ8
zEg(>AX>M)+A^8LW00093EC2ui01yBW0006!oQ{dZ5)Xl523omVP!0>~IFN{en<WO4
pnVuuN9r5#NA??=HeTsSr<o!k{nh8hT9AgQi#sXC~I2=X+06U&|EusJb

literal 0
HcmV?d00001

diff --git a/skins/plinn_doc.thumb.gif b/skins/plinn_doc.thumb.gif
new file mode 100644
index 0000000000000000000000000000000000000000..359ca22ecb17fbe0297c6115732d8ab766be49a2
GIT binary patch
literal 1054
zcmV+(1mXKfNk%w1VSoUD0FeU#V`_28*W~~J0RR90A^sRVLr+jyK0;+~Z|-y;P)SZ8
zEg(>AX>M)+A^8LW00093EC2ui0Du6100092oNuDb?GK}zv{r<(PPY3GhRjB4;)$kU
z$sXzp$4cAE_WjW;&i9aB@(G1N((s5YZXkiC^9hYgr&K2Z8Bv-Z)votz#XhtdCiskg
zUd1Ca!kmtS*KC3N&6VrDbn)Glw<kE&hxEtcFsLY1ICMAww<sCV=<^skxmd|_8JH=#
zdDEHrnHefG+EDs=3X{sP%1U&I8LQZu^15Iv%Sr3WtLgh!+f+vyg&U%)Fm<d-DEf;G
z^$bg;?7=MHY~5UyZL!_$%@;E+<^A0iK7k&k&i&P%H4P1~envlLe~nL_sTj6g{dT1T
zHc8;X6Yr`al%#OsvV!Xz0!;YO+(csk86yhvC{Y_ldH?Qx0vS@=NOC3vW}K5_<-v;(
zU&8Fr@CiyeJHg?US<>H0oBC98q#2-R8l^rd<aEOHOw^S8j<$qKbk9_(4W1gys%I<L
z1zwYa<#`rOsIp?av>nSf?NYgDegL(2wI)(UcC8B4NmpuMFn#X`d`kr}%Qk)u26a4i
z@1VkN4iEmKcxKnTfF*yt?1eHJ%bvA@c3ahO$JHnnPpF%^GV8o%TWh8O+d*8UDqKs*
zy}5D4&a`{qel1uxXRN$a_>KUavFhg3PdryDS_|po&X5029(+3Y>LRYo&8}qoU*@!*
zy9X~_HhIXz*H=+bAGLDfB=Oq+uic(^{neM>eX)%;+ke<4SYToYYRBMk?EUxNV-gy;
zpLhyJIG}#x1!$oi%Y8N9g$|b3A%@|}_u+=65oqFq8M5eNiw#!j-;Dz9W}}HX!kFTT
zD<X*Ej~>=Y;f^B$h@_4o8mVG@MD`e@j8sNP){^-7C?J$VN(o_+UBcI(m0+gFp_g8o
znI(Vbq3D5{O1dfFmLJF&B9FM$*#Vw8&Y)$Vd|Kcqka{8)Vw__V_8^*Ya;WH&WCBWO
zqL2zIDWfw^+2nVY61pg6jTV7eW|@k*X_z3Qx@uVGjS57mr$y=HpCVRj+KXuEO5%{J
zCaGzwy9(RunWWCjWUa2#@|rBA8Y+7vv~jTMERE8Bc`cS)=DDP{LGtx#s*Vzi?Y6m+
zi{7!!7CJ1jwwC2>EY#lmthu2f%Wko4*8A<G{eH%5y0Ic_s;%C_>#w}w`Z}(=_m<-?
z!ofasaJT-7Q8BK|T9Yxq;6gm{zSi0sZ5<tt{4c8%M{IG(CbO(@Ffu>Pu)5^V9E`v^
z6pS;#H4nUQ&kBocv%^B?4C~JL9__QSK;O)CWIrn%bkrCT{d7E9i_GcPT5sdEHDHH*
Y%-CeJVfNWrq^<T5Y_~m&4g~-JJ8J3{?f?J)

literal 0
HcmV?d00001

diff --git a/skins/plinn_file_upload.gif b/skins/plinn_file_upload.gif
new file mode 100644
index 0000000000000000000000000000000000000000..b689dad7f7db3745e715ec36cd327d2e2d5250e7
GIT binary patch
literal 386
zcmZ?wbhEHb6lV})xT?YM`t_HeKmR>^`1bSXpHH8@fA{X|$B#c=z54v%!JDsN|Ga<y
z?b)*rfByXc@ZtNNJ1?I<|M2hM|2J>G{Qdj?)2E-8FF(9`^~v??Pfwn_b>__N8#kVv
zJ9qcnx4*xB{eSZ0-K9$pZrytR^5v%=KmOgj_xi<)kGF5XxNzbAoH^V6g8>6IfZ|UU
zMg|5A1|5)LAU`p%&3Bkm;GrYce_}~dr;g=(KjGjAt4L-}fdwk5VQYH=QoLVk?N<+W
zU^Liy)_1ja0)vi@_z%I{Bp)t$DTyk#_<UvtQMnlJc!3Ch4n7sh7S~V#A!$}_77iu8
zX)f`MYOL(aYD^r9oI{1w*wk{>WY_en%WT}tuExT&HbkA1S&f%9|G*ArVO}ml_PM9h
ad6=2ma@hIzAJoXd{~-9$<0nrY8LR;*<fCl>

literal 0
HcmV?d00001

diff --git a/skins/plinn_icons/hidden_layer.gif b/skins/plinn_icons/hidden_layer.gif
new file mode 100644
index 0000000000000000000000000000000000000000..8b85acb848f08e9403a7498b3600f8d4d6bc6e37
GIT binary patch
literal 333
zcmZ?wbhEHb<YnMyxN63*d-v|=&!2z&`t{GBKkwhazj^cKqeqW+?AY=0<;xp4ZmeIw
z{@=fUuU@@cvSi7<d-o0>K78fMmCKhe-??+=#fukPwrp9wdiAwy*XGWhyKddOt5>g{
zK7D%U&YjDbFTZ{J_Q#JOFI>3r>({Txj~^dBdUVd5Ih!_ZTDEN2%9ShU&!4|y#fn|K
zcAYtMX2XUJd-v|$w{PFKZ{Hq1e0cx<{bR?Defjcb)~s2xXV3l*1`N1?;!hSv1_m<*
z9gq@`pBUJ(9A+1Is9Y1<;F2@5k~eFc)T4<S2270ZD$g42WTu66UKUsxw`Kxw<IJ-j
z%P+oJFu@^9ig|75;-87^A_`_KEL?SU;=*jAPNsUM40#$xM!d3p+VVV8nOW5g0-U(@
Ub)_{K7A|t`XJVS*;>chP09011bN~PV

literal 0
HcmV?d00001

diff --git a/skins/plinn_icons/imagebox.gif b/skins/plinn_icons/imagebox.gif
new file mode 100644
index 0000000000000000000000000000000000000000..cb90831781b1b33991862325e7aa45a51ddccffc
GIT binary patch
literal 301
zcmV+|0n+|QNk%w1VHp4!0J8u9h=_<YGczJ0A`uZ0L_|bYRaM>H-FJ6)s;a8HySwi0
z?wOgH%*@PYW@Z2Y0RR90A^8LV00000EC2ui02u%o000Eq@X1N5y;vER&=Oc^8Ac$U
z&jy$%W+XrYww40hY|9*y!W6*5pk*jD9tsU7kr<weh(jWo+$0=`%3;ebAUpuYMCmvf
zC{pIa`$aUU)$1WUNdp`0HNj{|%IOkm2@4Hd4TW9+1$}==flN$h3Tq7nObua;XAg#l
zl3tL51vX__3}bLf1q-2yFQpF&6sbN1p#=c1a}R*D8UPIo2n7cWx>*Z7z8Y@0#s&zm
z07%PJB-hZeJk?d$B+<JCv7zMTZQxk~4(sgg?(e(2T=VqxTL_ye=llHUBoP2RTpV^B

literal 0
HcmV?d00001

diff --git a/skins/plinn_icons/layer.gif b/skins/plinn_icons/layer.gif
new file mode 100644
index 0000000000000000000000000000000000000000..018d3e1aedd8b70b9a37f08865942319171e3aaa
GIT binary patch
literal 257
zcmV+c0sj6+Nk%w1VHp4!0J8u9RaI4HW@g>p-MhQHnVFf0h=?L0BFxOpL_|dM^YhWs
z(W<JdcXxLH00960{{R30A^8LV00000EC2ui02u%o000E8(8)=wtu5=#8x6+fUDha`
zN~*4E!!Y6^c}im6xa~2+%m@xzf^_2fWJ2Q1!H@-;K}U_KVLDw%>5!y(U7<-50N`Q}
zj?t;Ie12xtRAvDkjw9yo^wti`hcf^aRS11pdNU1yA{h>ZbRCcx2nYda5`C916_b>f
zOfR6IoTG|{69)$a0}BfT1O){z0Rdy4N>osaw-<&6k-r;I4hx~h#>ERj5(2Ev&d<*R
H0ucZ^3|VFS

literal 0
HcmV?d00001

diff --git a/skins/plinn_icons/textarea.gif b/skins/plinn_icons/textarea.gif
new file mode 100644
index 0000000000000000000000000000000000000000..21822d6d23da9ab95edde575269768ad443b81ff
GIT binary patch
literal 239
zcmZ?wbhEHblw^=(*v!Ci_Uzfackia9otZs*c6oXEs#U9Q-u%zN!0`V+h{ObnKUu(n
zIv^5c1_R3+fs>xA*Q&oh`+MenRV8_WNgWH<<#;n44)mCxmQtQKSxRuh&IOJetX2f5
z9C{;sgtM0|#D}xRsr~5V^j$uYy~jelx*sq_r=*osFPP_&pSj!Gsp(X)wM=vQ^{+*B
zrG+_7)%mR@^&*`O-MxkVwcc`5rI^!HCn_;>sO1^3&se-<Df7a-X{#hdI9ORWY}~YY
VGpoH2BkQi+d-m>OWE5nu1^_r<Si1lK

literal 0
HcmV?d00001

diff --git a/skins/plinn_icons/visible_layer.gif b/skins/plinn_icons/visible_layer.gif
new file mode 100644
index 0000000000000000000000000000000000000000..bde835ca453f8546f26eab2ea342d3dbdca4d283
GIT binary patch
literal 536
zcmZ?wbhEHb<YnMyc;?8ke*OBcuC7z3PTjqGHzFe9_3PJr_wGG*?AW$#+nSr3@7%d_
z`SRs=@7}FlyVlFgtE#FhAt7P;^5w^mACHfZudJ-xvSmw7PR^%KpLXutxo_XTr%#{e
z<>h5%Wj%lXeB;KACr_SSxpHN6baZTNY;|>YU0q#fX6B3;GcqzVu3o+B>+4%oRJ35h
zf)y)PoH%i!qM~By(xo$J&b)W;UT<%2a&q#E7cUwb8ft56o0^&e0s?mL-o0bTj?bSz
z-@0{c!h{JP9v<Jmee?A6{Qv(y!;l4vKUo+V7#taNKt2G)2?Kj+gJV;3ON%|L6|Zf#
zj-`pcqrE&MF9Q=hJBPll#so$V1x`gJ79#^oQ%Of%bvrw03qCtNRZ&4}H8uxMYlnSS
zB3e?8+}twz_Gt>S88`7Ti<_+zwy|N6YqA#8X1XfNARuAg%xY=QudHIlGoj6pi;MYf
IQzL^l0Ga8TlmGw#

literal 0
HcmV?d00001

diff --git a/skins/plinn_image_upload.gif b/skins/plinn_image_upload.gif
new file mode 100644
index 0000000000000000000000000000000000000000..9020ace866925f229563524ce23385584906356b
GIT binary patch
literal 1046
zcmZ?wbhEHb6lV})_}<QN{raO@x1KzB@bdQUr@w#y`SIh|wQCPQeE4$b&eQw%Up#s8
z`o)WPU%&pibm`v1hcDl~`!sFpRJ+2tPoKVtk1u=s_ESVu;j?FNpFe;1{nYl5(5#-$
zZikq>*!a>(vu2bfC#+bpB5=#CSFb+A?7Fw&)XmcBs)Mz~Gp@dRdgbcbv$witt-XHz
z`k_M?Pn@{6r(^P!D-Rybnoz#H$acoD+YcUYDM(&8f5E}MdzLI-GS9*1OHaYgn~#%r
zM?WixnSXuSvW*+|@4w(5RF<2U*VotYU9mVmp>+Gf!~4vQ_Fp~Rd+teFTidZ?M~@u2
zeDB`#Gf4>-F5EeN`sRY8vl<#2T%x+-;}e&ySm{vfd+^fz&!4~LoqgSN<n|pu>&m*i
z^-+;8S1+D;wD0r8yvw^cO_?&qqSEiEnU=*uvvcQepFDYe>eQ*nk00N%4G6dT7Q_}6
z6`e~@+`MVil&;R%^B13*KIilM_x^!tU%q^gPbfR&U|q8(ee2dOCapW}-@pI)&%d|P
zR&V<2*E_pkym<HAxwE@lYm*K?zWn&qu5Ftu>Zcz&c6G{>wg3PBXBY)^2?52QEQ|~c
z?F>2~+dz4Of#Wj+Kc|exh6M+kIfS)hR&1DXxSfyJP$Z(mq0y4xNk=fiL+NlAtAxyg
z2Mx)R{k*zeH47XX7EagTHClO@iMvCvY1bDI1BTZA87$iq4qQCm(!t?Yk@MrC%So;U
z%-$6zi<AR;H2h0iPCjUHb#H0-*3nqZ%CBghe8nf@K|4=KV{@5^WueOnX^9Yz3x*FC
z9lY)A61Sl<Sk<+cFUo|6li|RDc$1nPFHU}b?jt3YV8L_Zp@d?KWPHjBk4w**B~^+9
z4E}K+X`ZcFp2O0($<2%PD#JfMos`srtejjjA7b`yxX{STt+U0aA$q&Rj0=nX=lJcl
J`r5!?4FI>Zdcgny

literal 0
HcmV?d00001

diff --git a/skins/plinn_init_javascript_code.dtml b/skins/plinn_init_javascript_code.dtml
new file mode 100644
index 0000000..f83f7e8
--- /dev/null
+++ b/skins/plinn_init_javascript_code.dtml
@@ -0,0 +1,8 @@
+<!--
+    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
index 0000000..fd79963
--- /dev/null
+++ b/skins/plinn_macros.pt
@@ -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
index 0000000..bd0d5c7
--- /dev/null
+++ b/skins/plinn_multilingual.js.pt
@@ -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
index 0000000..855fecc
--- /dev/null
+++ b/skins/plinn_multilingual.js.pt.metadata
@@ -0,0 +1,2 @@
+[default]
+cache=HTTPCache
diff --git a/skins/plinndocument_edit_control.py b/skins/plinndocument_edit_control.py
new file mode 100644
index 0000000..a2e55b6
--- /dev/null
+++ b/skins/plinndocument_edit_control.py
@@ -0,0 +1,15 @@
+## Script (Python) "plinndocument_edit"
+##parameters=text='', file='', SafetyBelt='', **kw
+##title=Edit a document
+##
+
+from Products.CMFDefault.exceptions import EditingConflict, ResourceLockedError
+
+if text != context.text :
+	try:
+		context.edit( 'html', text, file, safety_belt = SafetyBelt)
+		return context.setStatus(True, 'Document changed.')
+	except (ResourceLockedError, EditingConflict), msg:
+		return context.setStatus(False, msg)
+else :
+	return context.setStatus(False, 'Nothing to change.')
diff --git a/skins/plinndocument_edit_form.py b/skins/plinndocument_edit_form.py
new file mode 100644
index 0000000..5484bcf
--- /dev/null
+++ b/skins/plinndocument_edit_form.py
@@ -0,0 +1,13 @@
+##parameters=change='', change_and_view='', ajax=''
+
+form = context.REQUEST.form
+if change and \
+   context.plinndocument_edit_control(**form) and \
+   context.setRedirect(context, 'object/edit', ajax=ajax) :
+	return
+elif change_and_view and \
+	 context.plinndocument_edit_control(**form) and \
+	 context.setRedirect(context, 'object/view', ajax=ajax) :
+	return
+
+return context.plinndocument_edit_template()
diff --git a/skins/plinndocument_edit_template.pt b/skins/plinndocument_edit_template.pt
new file mode 100644
index 0000000..ef54386
--- /dev/null
+++ b/skins/plinndocument_edit_template.pt
@@ -0,0 +1,69 @@
+<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
index 0000000..c14b64d
--- /dev/null
+++ b/skins/plinndocument_view.pt
@@ -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
index 0000000000000000000000000000000000000000..2ecfaa1917db89c0f0e51de86f3931edea24d6a5
GIT binary patch
literal 184
zcmeAS@N?(olHy`uVBq!ia0vp^JRr=$1|-8uW1a&kmUKs7M+Sy#H?H_c7y<c`C9V-A
z!TD(=<%vb942~)JNvR5+xryniL8*x;m4zo$ZGeirJY5_^DsCm^<maa=FiIFqTqM!4
z-55j)&-4i7IbuD-BhcprzeLx^pNy-MV(Jd8l$b65LCftsv*XbdbqZdFXZT&T**-Ed
a@i2)0QaiQw@|t|0sSKX3elF{r5}E*KcshCj

literal 0
HcmV?d00001

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