Browse Source

initial commit

Michael 10 years ago
commit
227d4f2324
100 changed files with 11235 additions and 0 deletions
  1. 7 0
      .gitignore
  2. 65 0
      glueIt
  3. 11 0
      readme.md
  4. 20 0
      src/allplatforms/classes/Clipboard.js
  5. 248 0
      src/allplatforms/classes/ColorPickerWidget.js
  6. 86 0
      src/allplatforms/classes/Compiler.js
  7. 231 0
      src/allplatforms/classes/CreationMenu.js
  8. 486 0
      src/allplatforms/classes/Document.js
  9. 105 0
      src/allplatforms/classes/DocumentMenu.js
  10. 267 0
      src/allplatforms/classes/Element.js
  11. 39 0
      src/allplatforms/classes/EmbedElement.js
  12. 91 0
      src/allplatforms/classes/FileManager.js
  13. 1143 0
      src/allplatforms/classes/FileManagerWindow.js
  14. 298 0
      src/allplatforms/classes/Grid.js
  15. 70 0
      src/allplatforms/classes/HTMLEditor.js
  16. 25 0
      src/allplatforms/classes/History.js
  17. 39 0
      src/allplatforms/classes/IFrameElement.js
  18. 195 0
      src/allplatforms/classes/ImageElement.js
  19. 21 0
      src/allplatforms/classes/Keyboard.js
  20. 103 0
      src/allplatforms/classes/MenuItem.js
  21. 246 0
      src/allplatforms/classes/MenuItemBackgroundColor.js
  22. 128 0
      src/allplatforms/classes/MenuItemBackgroundImg.js
  23. 107 0
      src/allplatforms/classes/MenuItemBackgroundRepeat.js
  24. 45 0
      src/allplatforms/classes/MenuItemCenter.js
  25. 39 0
      src/allplatforms/classes/MenuItemFileManager.js
  26. 43 0
      src/allplatforms/classes/MenuItemOutlines.js
  27. 107 0
      src/allplatforms/classes/MenuItemPageTitle.js
  28. 32 0
      src/allplatforms/classes/MenuItemPaste.js
  29. 33 0
      src/allplatforms/classes/MenuItemRedo.js
  30. 41 0
      src/allplatforms/classes/MenuItemSave.js
  31. 45 0
      src/allplatforms/classes/MenuItemSaveAs.js
  32. 105 0
      src/allplatforms/classes/MenuItemSaveRemote.js
  33. 32 0
      src/allplatforms/classes/MenuItemUndo.js
  34. 389 0
      src/allplatforms/classes/ResizeHandles.js
  35. 138 0
      src/allplatforms/classes/SCGUI.js
  36. 505 0
      src/allplatforms/classes/SCSystemBrowser.js
  37. 705 0
      src/allplatforms/classes/Selection.js
  38. 304 0
      src/allplatforms/classes/Server.js
  39. 172 0
      src/allplatforms/classes/SliderWidget.js
  40. 186 0
      src/allplatforms/classes/SuperGlue.js
  41. 440 0
      src/allplatforms/classes/TextEditor.js
  42. 49 0
      src/allplatforms/classes/TextElement.js
  43. 110 0
      src/allplatforms/classes/Widget.js
  44. 61 0
      src/allplatforms/classes/WidgetBackgroundColor.js
  45. 65 0
      src/allplatforms/classes/WidgetBorder.js
  46. 61 0
      src/allplatforms/classes/WidgetBorderColor.js
  47. 66 0
      src/allplatforms/classes/WidgetBorderRadius.js
  48. 33 0
      src/allplatforms/classes/WidgetCopy.js
  49. 51 0
      src/allplatforms/classes/WidgetDelete.js
  50. 72 0
      src/allplatforms/classes/WidgetEditHTML.js
  51. 136 0
      src/allplatforms/classes/WidgetIFrame.js
  52. 103 0
      src/allplatforms/classes/WidgetImgDimensions.js
  53. 158 0
      src/allplatforms/classes/WidgetImgLink.js
  54. 135 0
      src/allplatforms/classes/WidgetImgSrc.js
  55. 135 0
      src/allplatforms/classes/WidgetLayerBottom.js
  56. 141 0
      src/allplatforms/classes/WidgetLayerTop.js
  57. 74 0
      src/allplatforms/classes/WidgetLock.js
  58. 66 0
      src/allplatforms/classes/WidgetOpacity.js
  59. 82 0
      src/allplatforms/classes/WidgetPadding.js
  60. 189 0
      src/allplatforms/classes/WidthMarkers.js
  61. 269 0
      src/allplatforms/classes/Window.js
  62. 95 0
      src/allplatforms/classes/WindowManager.js
  63. 33 0
      src/allplatforms/classes/_ClassTemplate.js
  64. 16 0
      src/allplatforms/css/ColorPickerWidget.css
  65. 110 0
      src/allplatforms/css/CreationMenu.css
  66. 27 0
      src/allplatforms/css/Document.css
  67. 17 0
      src/allplatforms/css/DocumentMenu.css
  68. 389 0
      src/allplatforms/css/FileManagerWindow.css
  69. 36 0
      src/allplatforms/css/Grid.css
  70. 62 0
      src/allplatforms/css/HTMLEditor.css
  71. 91 0
      src/allplatforms/css/MenuItem.css
  72. 18 0
      src/allplatforms/css/MenuItemBackgroundColor.css
  73. 45 0
      src/allplatforms/css/MenuItemBackgroundImg.css
  74. 50 0
      src/allplatforms/css/MenuItemBackgroundRepeat.css
  75. 3 0
      src/allplatforms/css/MenuItemCenter.css
  76. 3 0
      src/allplatforms/css/MenuItemFileManager.css
  77. 3 0
      src/allplatforms/css/MenuItemOutlines.css
  78. 37 0
      src/allplatforms/css/MenuItemPageTitle.css
  79. 3 0
      src/allplatforms/css/MenuItemPaste.css
  80. 3 0
      src/allplatforms/css/MenuItemRedo.css
  81. 3 0
      src/allplatforms/css/MenuItemSave.css
  82. 3 0
      src/allplatforms/css/MenuItemSaveAs.css
  83. 40 0
      src/allplatforms/css/MenuItemSaveRemote.css
  84. 3 0
      src/allplatforms/css/MenuItemUndo.css
  85. 83 0
      src/allplatforms/css/ResizeHandles.css
  86. 36 0
      src/allplatforms/css/Selection.css
  87. 44 0
      src/allplatforms/css/SliderWidget.css
  88. 25 0
      src/allplatforms/css/SuperGlue.css
  89. 237 0
      src/allplatforms/css/TextEditor.css
  90. 90 0
      src/allplatforms/css/Widget.css
  91. 3 0
      src/allplatforms/css/WidgetBackgroundColor.css
  92. 4 0
      src/allplatforms/css/WidgetBorder.css
  93. 3 0
      src/allplatforms/css/WidgetBorderColor.css
  94. 3 0
      src/allplatforms/css/WidgetBorderRadius.css
  95. 3 0
      src/allplatforms/css/WidgetCopy.css
  96. 3 0
      src/allplatforms/css/WidgetDelete.css
  97. 3 0
      src/allplatforms/css/WidgetEditHTML.css
  98. 45 0
      src/allplatforms/css/WidgetIFrame.css
  99. 70 0
      src/allplatforms/css/WidgetImgDimensions.css
  100. 45 0
      src/allplatforms/css/WidgetImgLink.css

+ 7 - 0
.gitignore

@@ -0,0 +1,7 @@
+.DS_Store
+dist
+*~
+\.\#*
+\#*\#
+
+build/

+ 65 - 0
glueIt

@@ -0,0 +1,65 @@
+#!/bin/sh
+
+
+case "$1" in
+
+
+    "clean") 
+        echo "removing ./build dir..." 
+        rm -R ./build
+        ;;
+
+
+    "build") 
+        echo "building SuperGlue clients..." 
+        mkdir -p ./build
+        mkdir -p ./build/firefox
+        mkdir -p ./build/chromium
+
+
+        cp -R ./src/firefox/* ./build/firefox
+        cp -R ./src/chromium/* ./build/chromium
+
+        cp -R ./src/allplatforms/* ./build/firefox/data/superglue-client
+        cp -R ./src/allplatforms/* ./build/chromium/superglue-client
+        ;;
+
+
+    "testFF") 
+        echo "Testing Firefox add-on..."
+        cd ./tools/firefox-addon-sdk-1.16
+        source bin/activate
+        cd ../..
+        cd ./build/firefox
+        cfx run
+        cd ../..
+        ;;
+        
+
+    "pkgFF")
+        echo "Packaging Firefox add-on..."
+        cd ./tools/firefox-addon-sdk-1.16
+        source bin/activate
+        cd ../..
+        cd ./build/firefox
+        cfx xpi
+        cd ../..
+        ;;
+
+
+   *) 
+        echo "Usage: $0"
+        echo " "
+        echo "  clean    Remove build dir"
+        echo "  build    Build SuperGlue clients"
+        echo " "
+        echo "  testFF   Test Firefox add-on in sandboxed browser"
+        echo "  pkgFF    Make Firefox add-on package (./build/SuperGlue.xpi)"
+        echo "  (----    To package Chromium extension, go there to chrome://extensions)" 
+        echo " "
+        echo "ATTENTION!! Call $0 only in root of the project directory!"
+        echo " "
+        ;;
+
+
+esac

+ 11 - 0
readme.md

@@ -0,0 +1,11 @@
+## Clientplugin - browser add-on/extension for SuperGlue
+### [Documentation](http://git.superglue.it/superglue/documentation/wikis/home)
+   
+
+Take a look a [SuperGlue project summary](http://git.superglue.it/superglue/documentation/wikis/home) to learn more about the project.  
+
+
+http://superglue.it
+
+
+To build SuperGlue clients, go to the root directory of this repo, and execute ./glueIt

+ 20 - 0
src/allplatforms/classes/Clipboard.js

@@ -0,0 +1,20 @@
+SC.loadPackage({ 'Clipboard': {
+
+    comment: 'I manage the clipboard for the editing tool.',
+
+
+
+    methods: {
+
+        init: { 
+            comment:    'method comment',
+            code:       function(){
+
+            }
+        }
+
+
+    }
+
+
+}});

+ 248 - 0
src/allplatforms/classes/ColorPickerWidget.js

@@ -0,0 +1,248 @@
+SC.loadPackage({ 'ColorPickerWidget': {
+
+    comment: 'I am a trait class for Widget classes and provide the functionality for a ColorPicker.',
+
+    traits: ['Widget'],
+
+    
+    properties: {
+
+        widgetPanel:    { comment: 'I store the DOMElement containing the panel of controls of the widget.' },
+
+        isWidgetActive: { comment: 'Wether the widget is active.',
+                          transform: function(aBoolean){
+                            
+                                if(aBoolean){
+
+                                    if(this.get('widgetPanel').parentNode !== this.get('widgetMenu')){
+                                        this.get('widgetMenu').appendChild(this.get('widgetPanel'));
+                                    }
+                                    this.get('widgetMenu').classList.add('active');
+
+                                    // update Value
+
+                                }else{
+
+                                    if(this.get('widgetPanel').parentNode === this.get('widgetMenu')){
+                                        this.get('widgetMenu').removeChild(this.get('widgetPanel'));
+                                    }
+                                    this.get('widgetMenu').classList.remove('active');
+                                
+                                }
+                                return aBoolean
+                          }
+                        }
+
+    },
+
+    methods: {
+
+        initColorPickerWidget: { 
+            comment:    'I init the widget as a ColorPickerWidget.',
+            code:       function(colorPickerConfig){
+
+                var widgetPanel = '<div class="sg-editing-widget-colorPicker-panel">'
+                                        +'<div class="sg-editing-widget-panel">'
+                                            +'<div class="sg-widget-triangle-up"></div>'
+                                            +'<div class="sg-colorpicker-container"></div>'
+                                        +'</div>'
+                                    +'</div>',
+
+
+                    self        = this,
+                    widgetPanel = (new DOMParser()).parseFromString(widgetPanel, 'text/html').body.firstChild;
+                
+                widgetPanel.querySelector('.sg-editing-widget-panel').addEventListener('mouseup', function(evt){
+                    colorPickerConfig.theSelection.set({ activeWidget: self });
+                    evt.stopPropagation();
+                }, false);
+
+                this.set({ 
+                    widgetPanel: widgetPanel
+                });
+
+
+
+                // From flexicolorPicker
+
+
+
+                var colorpickerContainer = widgetPanel.querySelector('.sg-colorpicker-container');
+                var colorpicker;
+
+                // Inputs
+
+                var colorpickerInputR,
+                    colorpickerInputB,
+                    colorpickerInputB,
+                    colorpickerInputHex;
+
+
+
+                var start = function() {
+                    
+                    var colorpickerElement = document.createElement('div');
+                    colorpickerElement.classList.add('sg-colorpicker');
+                    colorpickerContainer.appendChild(colorpickerElement);
+
+                    var colorpickerInputContainer = document.createElement('div');
+                        colorpickerInputContainer.classList.add('sg-colorpicker-input-container');
+
+                    colorpickerInputR = document.createElement('input');
+                        colorpickerInputR.setAttribute('type', 'number');
+                        colorpickerInputR.addEventListener('change', function() {
+                            updatePicker(ColorPicker.rgb2hex({ r: this.value, g: colorpickerInputG.value, b: colorpickerInputB.value }));
+                        });
+                    colorpickerInputContainer.appendChild(colorpickerInputR);
+
+                    colorpickerInputG = document.createElement('input');
+                        colorpickerInputG.setAttribute('type', 'number');
+                        colorpickerInputG.addEventListener('change', function() {
+                            updatePicker(ColorPicker.rgb2hex({ r: colorpickerInputR.value, g: this.value, b: colorpickerInputB.value }));
+                        });
+                    colorpickerInputContainer.appendChild(colorpickerInputG);
+
+                    colorpickerInputB = document.createElement('input');
+                        colorpickerInputB.setAttribute('type', 'number');
+                        colorpickerInputB.addEventListener('change', function() {
+                            updatePicker(ColorPicker.rgb2hex({ r: colorpickerInputR.value, g: colorpickerInputG.value, b: this.value }));
+                        });
+                    colorpickerInputContainer.appendChild(colorpickerInputB);
+
+                    colorpickerInputHex = document.createElement('input');
+                        colorpickerInputHex.setAttribute('type', 'text');
+                        colorpickerInputHex.addEventListener('change', function() {
+                            updatePicker(this.value);
+                        });
+                    colorpickerInputContainer.appendChild(colorpickerInputHex);
+
+                    colorpickerContainer.appendChild(colorpickerInputContainer);
+
+
+                    colorpicker = ColorPicker(colorpickerElement, updateColor);
+
+                    updatePicker(initialColor);
+
+                    var topColors = getMostUsedColors();
+                    var topColorsContainer = document.createElement('div');
+                        topColorsContainer.classList.add('sg-colorpicker-top-colors');
+
+                    for (var i=0; i<topColors.length; i++) {
+                        var topColorElement = document.createElement('span');
+                            topColorElement.style.backgroundColor = topColors[i].color;
+                            topColorElement.addEventListener('mousedown', function() {
+                                updatePicker(rgbString2Hex(this.style.backgroundColor));
+                            });
+                        topColorsContainer.appendChild(topColorElement);
+                    }
+
+                    colorpickerContainer.appendChild(topColorsContainer);
+
+                    var transparentElement = document.createElement('div');
+                        transparentElement.classList.add('sg-colorpicker-transparent');
+                        transparentElement.addEventListener('mousedown', function() {
+                            colorPickerConfig.setCallback.call(this, '');
+                        });
+                    colorpickerContainer.appendChild(transparentElement);
+                    
+                }
+
+
+                var updateColor = function(hex) {
+                    
+                    var rgb = ColorPicker.hex2rgb(hex);
+
+                    colorpickerInputHex.value = hex;
+                    
+                    colorpickerInputR.value = rgb.r;
+                    colorpickerInputG.value = rgb.g;
+                    colorpickerInputB.value = rgb.b;
+                    
+                    colorPickerConfig.setCallback.call(this, 'rgb('+rgb.r+', '+rgb.g+', '+rgb.b+')');
+                }
+
+                var updatePicker = function(hex) {
+                    
+                    colorpicker.setHex(hex);
+                }
+
+                var rgbString2Hex = function(rgbString) {
+                     if(rgbString === ''){
+                        return '';
+                     }
+                     if (  rgbString.search("rgb") == -1 ) {
+                          return rgbString;
+                     } else {
+                          rgbString = rgbString.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+))?\)$/);
+                          function hex(x) {
+                               return ("0" + parseInt(x).toString(16)).slice(-2);
+                          }
+                          return "#" + hex(rgbString[1]) + hex(rgbString[2]) + hex(rgbString[3]); 
+                     }
+                };
+
+                var getMostUsedColors = function() {
+                    
+                    var elements = SuperGlue.get('document').get('children');
+                    var colorArray = [];
+
+                    for (var i=0; i<elements.length; i++) {
+                        if ( elements[i].get('node').style.backgroundColor.length ) {
+                            colorArray.push(elements[i].get('node').style.backgroundColor);
+                        }
+                        if ( elements[i].get('node').style.borderColor.length ) {
+                            colorArray.push(elements[i].get('node').style.borderColor);
+                        }
+                    }
+
+                    var frequencyObject = {};
+                    for( var v in colorArray ) {
+                        frequencyObject[colorArray[v]]=(frequencyObject[colorArray[v]] || 0)+1;
+                    }
+
+                    var frequencyArray = [];
+                    for ( var f in frequencyObject ) {
+                        var newObj = {};
+                        newObj["color"] = f;
+                        newObj["count"] = frequencyObject[f]
+                        frequencyArray.push(newObj);
+                    }
+
+                    function compare(a,b) {
+                        if (a.count < b.count)
+                            return 1;
+                        if (a.count > b.count)
+                            return -1;
+                        return 0;
+                    }
+
+                    frequencyArray.sort(compare);
+
+                    if ( frequencyArray.length > 5 ) {
+                        frequencyArray.slice(0, 5);
+                    }
+
+                    return frequencyArray;
+
+                }
+
+                var initialColor = rgbString2Hex(colorPickerConfig.initialColor);
+                start();
+
+
+
+
+
+
+
+                // End from flexicolorPicker
+
+            }
+
+        }
+
+
+    }
+
+
+}});

+ 86 - 0
src/allplatforms/classes/Compiler.js

@@ -0,0 +1,86 @@
+SC.loadPackage({ 'Compiler': {
+
+
+    comment: 'I compile W3C HTML5 conpliant self-contained documents from this editor\'s current document.',
+
+    properties: {
+
+        pageAsHTML5: {
+
+            comment: 'I compile to HTML5.',
+            transform: function(theDocument){
+
+                var thePage = '';
+
+                thePage += '<!DOCTYPE html>\n<html>';
+
+                thePage +=   '\n\t<head>'
+                            +'\n\t\t<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">'
+                            +'\n\t\t<meta name="generator" content="SuperGlue" data-superglue-version="'
+                                    +SuperGlue.class.get('version')
+                                    +'" data-superglue-settings="">'
+                            +'\n\t\t<title>'
+                                    +document.getElementsByTagName('title')[0].innerHTML
+                                    +'</title>'
+                            +'\n\t\t<style type="text/css" data-superglue="default-css">'
+                            +'\n\t\t\tbody { margin: 0px; padding: 0px; }'
+                            +'\n\t\t\t#sg-page { position: relative; top: 0px; }'
+                            +'\n\t\t\t#sg-page.sg-page-centered { margin: 0px auto; }'
+                            +'\n\t\t\t.sg-element { position: absolute; overflow: hidden; }'
+                            +'\n\t\t</style>'
+                            +'\n\t</head>';
+                
+                thePage += '\n\t<body style="'+ document.body.getAttribute('style')
+                                                                .replace(document.location.origin, '')
+                                                                .replace('url("', 'url(')
+                                                                .replace('");', ');')
+
+                            +'">';
+        
+                thePage += '\n\t\t<div id="sg-page" '
+                            + (theDocument.get('layout').centered 
+                                ? ('class="sg-page-centered" style="width: ' + theDocument.get('layout').width + 'px;" ')
+                                : '')
+                            + 'data-superglue-grid="'
+                                + (theDocument.get('grid').get('active') ? 'on' : 'off')
+                                + '/'+
+                                + theDocument.get('grid').get('gridSize')
+                                + 'px">';
+                
+
+                for(var children = theDocument.get('children'),
+                        i = 0, l = children.length; i < l; i++){
+
+                    thePage += children[i].do('renderYourself', { indent: 4 })
+
+                }
+                            
+                
+                thePage += '\n\t\t</div>\n\t</body>\n</html>';
+
+                
+                return thePage;
+
+            }
+
+        }
+
+    },
+
+    methods: {
+
+        init: {
+            comment:  'I init the compiler (currently no options, only html5).',
+            code:     function(){
+
+
+                this.set({ pageAsHTML5: SuperGlue.get('document') });
+
+
+            }
+
+        }
+
+    }
+
+}});

+ 231 - 0
src/allplatforms/classes/CreationMenu.js

@@ -0,0 +1,231 @@
+SC.loadPackage({ 'CreationMenu': {
+
+    comment: 'I am the CreationMenu which is shown when the user drags over the document background.',
+
+    sharedProperties: {
+        menuContainer: { initValue: '<div id="sg-editing-creation-menu"><div id="sg-editing-creation-menu-items"></div></div>' },
+    },
+
+    properties: {
+        myNode:        { comment: 'I hold my DOM node.' },
+        myItemsNode:   { comment: 'I hold the DOM node for the items container.' },
+        startX:        { transform: function(anInt){
+                                
+                                return anInt;
+                            }
+                       },
+        startY:        { transform: function(anInt){
+                                return anInt;
+                            }
+                       },
+        stopX:         { transform: function(anInt){
+
+                                var startX = this.get('startX'),
+                                    myNode = this.get('myNode'),
+                                    grid     = SuperGlue.get('document').get('grid'),
+                                    gridSize = grid.get('gridSize'),
+                                    left, width;
+
+                                if(anInt > startX){
+                                    if(grid.get('active')){
+                                        left  = Math.floor(startX / gridSize) * gridSize;
+                                        width = Math.ceil((anInt - startX) / gridSize) * gridSize;
+                                    }else{
+                                        left  = startX;
+                                        width = anInt - startX;
+                                    }
+                                }else{
+                                    if(grid.get('active')){
+                                        left  = Math.floor(anInt / gridSize) * gridSize;
+                                        width = Math.ceil(startX / gridSize) * gridSize - Math.floor(anInt / gridSize) * gridSize;
+                                    }else{
+                                        left  = anInt;
+                                        width = startX - anInt;
+                                    }
+                                }
+
+                                myNode.style.width = width + 'px';
+                                myNode.style.left  = left  + 'px';
+                                
+
+                                return anInt;
+                            }
+                       },
+        stopY:         { transform: function(anInt){
+
+                                var startY = this.get('startY'),
+                                    myNode = this.get('myNode'),
+                                    grid     = SuperGlue.get('document').get('grid'),
+                                    gridSize = grid.get('gridSize'),
+                                    top, height;
+
+                                if(anInt > startY){
+                                    if(grid.get('active')){
+                                        top    = Math.floor(startY / gridSize) * gridSize;
+                                        height = Math.ceil((anInt - startY) / gridSize) * gridSize;
+                                    }else{
+                                        top    = startY;
+                                        height = anInt - startY;
+                                    }
+                                }else{
+                                    if(grid.get('active')){
+                                        top    = Math.floor(anInt / gridSize) * gridSize;
+                                        height = Math.ceil(startY / gridSize) * gridSize - Math.floor(anInt / gridSize) * gridSize;
+                                    }else{
+                                        top    = anInt;
+                                        height = startY - anInt;
+                                    }
+                                }
+
+                                myNode.style.height = height + 'px';
+                                myNode.style.top    = top    + 'px';
+                                
+
+                                return anInt;
+                            }
+                       },
+
+        active:        { comment: 'I store my state as a boolean.' }
+
+    },
+
+    methods: {
+
+    	init: { 
+    		comment: 	'I init myself.',
+    		code: 		function(){
+
+                var self = this,
+                    myNode        = (new DOMParser()).parseFromString(this.class.get('menuContainer'), 'text/html').body.firstChild,
+                    myItemsNode   = myNode.firstChild,
+                    
+                    menuItemsClasses = (function(){ 
+                                            return SC.getClasses().filter(function(classname){
+                                                return SC.behavesLike(classname, 'Element');
+                                            });
+                                        }).call(this),
+                    menuItem = null;
+
+
+                for(var i = menuItemsClasses.length - 1; i >= 0; i--){
+
+                    menuItem = (new DOMParser()).parseFromString(
+                                        SC.getSharedProperty(menuItemsClasses[i], 'creationMenuItem'),
+                                        'text/html').body.firstChild;
+                    
+                    var onMouseUp = (function(classname){
+                        return function(evt){
+                            self.do('performCreation', classname);
+                        }
+                    }).call(this, menuItemsClasses[i])
+                    menuItem.addEventListener('mouseup', onMouseUp, false);
+
+                    myItemsNode.appendChild(menuItem);
+
+                }
+
+                
+                this.set({
+                    myNode:         myNode,
+                    myItemsNode:    myItemsNode
+                });
+
+    		}
+    	},
+
+        trigger: {
+            comment: 'Show me at { startX: anInt, startY: anInt }',
+            code: function(myCoordinates){
+
+                var self   = this,
+                    myNode = this.get('myNode'),
+                    myItemsNode = this.get('myItemsNode'),
+                    editingContainer = SuperGlue.get('document').get('editingContainer');
+                    
+                myItemsNode.style.display = 'none';
+
+                this.set({ 
+                    startX: myCoordinates.startX,
+                    startY: myCoordinates.startY
+                })
+                
+                if(myNode.parentNode !== editingContainer){
+                    editingContainer.appendChild(myNode);
+                }
+                this.set({ active: true })
+                
+
+            }
+        },
+
+
+
+
+        showItems: {
+            comment: 'After dragging has finished, I am called to show the menu items to create an instance of a Element class.',
+            code: function(){
+
+                var myItemsNode = this.get('myItemsNode')
+                    westSide   = this.get('stopX') < this.get('startX'),
+                    northSide  = this.get('stopY') < this.get('startY');
+
+                myItemsNode.className = '';
+
+                if(westSide){
+                    if(northSide){
+                        myItemsNode.classList.add('sg-editing-creation-menu-nw');
+                    }else{
+                        myItemsNode.classList.add('sg-editing-creation-menu-sw');
+                    }
+                }else{
+                    if(northSide){
+                        myItemsNode.classList.add('sg-editing-creation-menu-ne');
+                    }else{
+                        myItemsNode.classList.add('sg-editing-creation-menu-se');
+                    }
+                }
+
+                myItemsNode.style.display = 'block';
+                
+            }
+        },
+
+        close: {
+            comment: 'Hide me!',
+            code: function(){
+
+                var myNode           = this.get('myNode'),
+                    editingContainer = SuperGlue.get('document').get('editingContainer');
+
+                if(myNode.parentNode === editingContainer){
+                    editingContainer.removeChild(myNode);
+                }
+                this.set({ active: false });
+
+            }
+        },
+
+        performCreation: {
+            comment: 'Tell the document to create a new Element.',
+            code: function(classname){
+                
+                var myNodeStyle = this.get('myNode').style;
+                
+                SuperGlue.get('document').do('createNewElement', {
+                    classname:  classname,
+                    top:        parseInt(myNodeStyle.top),
+                    left:       parseInt(myNodeStyle.left),
+                    width:      parseInt(myNodeStyle.width),
+                    height:     parseInt(myNodeStyle.height)
+                });
+
+
+            }
+        }
+
+
+
+    }
+
+
+}});

+ 486 - 0
src/allplatforms/classes/Document.js

@@ -0,0 +1,486 @@
+SC.loadPackage({ 'Document': {
+
+    comment: 'I am the document the user works on.',
+
+    properties: {
+
+        pageContainer:      { comment: 'I store the div element containing the pages\' elements.' },
+        editingContainer:   { comment: 'I am a container holding all user interface components of the editing tool.'},
+        children:           { comment: 'I care for my (array of) children, which are the instances of some Element class.' },
+
+        showOutlines:       { comment: 'Shall I put outlines around all my child Elements?', 
+                              transform: function(aBoolean){
+                                if(aBoolean){
+                                    this.get('pageContainer').classList.add('sg-editing-outlines');
+                                    this.get('widthMarkers').set({ visible: true });
+                                    this.get('grid').set({ visible: true });
+                                }else{
+                                    this.get('pageContainer').classList.remove('sg-editing-outlines');
+                                    this.get('widthMarkers').set({ visible: false });
+                                    this.get('grid').set({ visible: false });
+                                }
+                                return aBoolean;
+                              }
+                            },
+
+        interactionInProgress:  { comment: 'Some interaction require a modal change of editing state, while the interaction is in progress. This concerns all interactions which listen for mouse moves.', 
+
+                              transform: function(aBoolean){
+                                if(aBoolean){
+                                    this.get('pageContainer').classList.add('sg-editing-block-events');
+                                    this.get('documentMenu').do('close');
+                                    this.get('creationMenu').do('close');
+                                }else{
+                                    this.get('pageContainer').classList.remove('sg-editing-block-events');
+                                }
+                                return aBoolean;
+                              }
+                            },
+
+        documentMenu:       { comment: 'I hold the DocumentMenu.' },
+        creationMenu:       { comment: 'I hold the CreationMenu.' },
+        widthMarkers:       { comment: 'When the document has a defined width, WidthMarkers visualize this and provide interaction.' },
+        grid:               { comment: 'The document always has a Grid to arrange its Elements.' },
+        
+        layout:             { comment: 'The document manages the basic layout: it can either be (centered with a defined width) or can be (of undefined dimensions). To change this property give me a newConfig like { centered: true, width: \'70%\' } or { centered: false }.',
+                              transform: function(newConfig){
+
+                                var pageContainer    = this.get('pageContainer'),
+                                    editingContainer = this.get('editingContainer');
+
+                                if(newConfig.centered){
+
+                                    if(newConfig.width === undefined){
+                                        this.do('cropEmptySpaceOnLeftSide');
+                                        newConfig.width = this.do('getMinWidth');
+                                    }
+
+                                    newConfig.width = newConfig.width > 140 ? newConfig.width : 140;
+
+                                    pageContainer.classList.add('sg-page-centered');
+                                    editingContainer.classList.add('sg-editing-container-centered');
+                                    pageContainer.style.width = editingContainer.style.width = newConfig.width + 'px';
+
+                                    this.get('widthMarkers').set({ active: true });
+
+                                }else{
+
+                                    pageContainer.classList.remove('sg-page-centered');
+                                    editingContainer.classList.remove('sg-editing-container-centered');
+                                    pageContainer.style.width = editingContainer.style.width = null;
+
+                                    this.get('widthMarkers').set({ active: false });
+
+                                }
+
+                                var returnConfig = { centered: newConfig.centered, width: newConfig.width };
+                                this.get('grid').do('updateDimensions', returnConfig);
+                                return returnConfig;
+
+                              }
+                            }
+
+        
+
+    },
+
+    methods: {
+
+        init: { 
+            comment:    'I set up the Document during system initialization, called from SuperGlue>>init.',
+            code:       function(){
+
+                var pageContainer    = this.set({  pageContainer: document.body.querySelector('#sg-page') }),
+                    editingContainer = this.set({  editingContainer: (function(){
+                                                        var editingContainer = document.createElement('div');
+                                                        editingContainer.setAttribute('id', 'sg-editing-container');
+                                                        document.body.appendChild(editingContainer);
+                                                        return editingContainer;
+                                                    }).call(this)
+                                        });
+
+                this.set({ 
+
+                    documentMenu:       SC.init('DocumentMenu'),
+
+                    creationMenu:       SC.init('CreationMenu'),
+
+                    widthMarkers:       SC.init('WidthMarkers', editingContainer),
+
+                    grid:               SC.init('Grid', this)
+
+                });
+
+                this.set({
+
+                    layout:             {
+                                            centered:   pageContainer.classList.contains('sg-page-centered'),
+                                            width:      parseInt(pageContainer.style.width)
+                                        }
+                });
+
+
+            }
+        },
+
+        setUpWorkspace: {
+            comment:    'I wake up my children from the DOM and prepare the document for work.',
+            code:       function(){
+
+                this.set({ 
+
+                    children:   (function(){
+                                    var nodeList = this.get('pageContainer').querySelectorAll('.sg-element'),
+                                        children = [];
+                                    for (var i = 0, l = nodeList.length; i < l; ++i) {
+                                        children.push(SC.do('Element', 'awakeFromDOM', nodeList.item(i)));
+                                    }
+                                    return children;
+                                }).call(this),
+                    
+                    showOutlines: document.querySelector('meta[name=generator]').getAttribute('data-superglue-settings').indexOf('wireframe') > -1
+
+                });
+
+                this.do('afterLayoutHasChanged');
+
+                this.do('registerEventListeners');
+
+            }
+        },
+
+        createNewElement: {
+            comment: 'I create a new Element, with the arguments configuration { classname: aString, top: anInt, left: anInt, width: anInt, height: anInt }.',
+            code: function(configuration){
+
+                var protoHTML = SC.getSharedProperty(configuration.classname, 'protoHTML'),
+                    newNode   = (new DOMParser()).parseFromString(protoHTML, 'text/html').body.firstChild,
+                    newChild;
+
+                this.get('pageContainer').appendChild(newNode);
+                newChild = SC.do('Element', 'awakeFromDOM', newNode)
+                newChild.set({
+                    top:    configuration.top,
+                    left:   configuration.left,
+                    width:  configuration.width,
+                    height: configuration.height
+                })
+
+                this.get('children').push(newChild);
+
+                this.do('afterLayoutHasChanged');
+
+
+            }
+        },
+
+        removeElement: {
+            comment: 'I remove an Element.',
+            code: function(anElement){
+
+                var indexOfElement = this.get('children').indexOf(anElement);
+                if(indexOfElement >= 0){
+                    this.get('children').splice(indexOfElement, 1);
+                    this.get('pageContainer').removeChild(anElement.get('node'));
+                }
+                this.do('afterLayoutHasChanged');
+
+            }
+        },
+
+
+        layerUp: {
+            comment: 'I move an Element upwards in the layer order.',
+            code: function(anElement){
+
+                var pageContainer = this.get('pageContainer'),
+                    children      = this.get('children'),
+                    childIndex    = children.indexOf(anElement),
+                    swapTemp;
+
+                if(childIndex === (children.length - 1)){
+                    return; 
+                }
+
+                swapTemp = children[childIndex + 1];
+                children[childIndex + 1] = children[childIndex];
+                children[childIndex] = swapTemp;
+                
+                pageContainer.insertBefore(
+                    children[childIndex + 1].get('node'),
+                    children[childIndex].get('node').nextElementSibling
+                );
+
+            }
+        },
+
+
+        layerDown: {
+            comment: 'I move an Element downwards in the layer order.',
+            code: function(anElement){
+
+                var pageContainer = this.get('pageContainer'),
+                    children      = this.get('children'),
+                    childIndex    = children.indexOf(anElement),
+                    swapTemp;
+
+                if(childIndex === 0){
+                    return; 
+                }
+
+                swapTemp = children[childIndex - 1];
+                children[childIndex - 1] = children[childIndex];
+                children[childIndex] = swapTemp;
+                
+                pageContainer.insertBefore(
+                    children[childIndex - 1].get('node'),
+                    children[childIndex].get('node')
+                );
+
+            }
+        },
+
+
+        getMinWidth: {
+            comment: 'I calculate the minimal width I need to fit in all my child Elements.',
+            code: function(){
+                var minWidth = 0,
+                    children = this.get('children');
+                for(var i = 0, l = children.length; i < l; i++){
+                    var childWidth = children[i].get('left') + children[i].get('width');
+                    minWidth = (childWidth > minWidth) ? childWidth : minWidth;
+                }
+                return minWidth;
+            }
+        },
+
+
+        getMinHeight: {
+            comment: 'I calculate the minimal height to fit in all my child Elements.',
+            code: function(){
+                var minHeight = 0,
+                    children  = this.get('children');
+                for(var i = 0, l = children.length; i < l; i++){
+                    var childHeight = children[i].get('top') + children[i].get('height');
+                    minHeight = (childHeight > minHeight) ? childHeight : minHeight;
+                }
+                return minHeight;
+            }
+        },
+
+        getMostLeft: {
+            comment: 'I get the left value of the most left positioned of all my child Elements.',
+            code: function(){
+                var children = this.get('children');
+                if(children.length === 0){ return 0; }
+                var mostLeft = children[0].get('left');
+                for(var i = 0, l = children.length; i < l; i++){
+                    var childLeft = children[i].get('left');
+                    mostLeft = (childLeft < mostLeft) ? childLeft : mostLeft;
+                }
+                return mostLeft;
+            }
+        },
+
+        cropEmptySpaceOnLeftSide: {
+            comment: 'I crop the empty space on the left side of the page. Should be called when centering the page.',
+            code:    function(){
+                
+                var children = this.get('children');
+                if(children.length === 0){ return 0; }
+
+                for(var cropWidth = this.do('getMostLeft'), i = 0, l = children.length; i < l; i++){
+
+                    children[i].set({ left: children[i].get('left') - cropWidth });
+
+                }
+
+                this.do('afterLayoutHasChanged');
+            }
+        },
+
+        afterLayoutHasChanged: {
+            comment: 'After the layout has changed (resizing, moving, or adding anything), I must be called to ensure the layout constraints.',
+            code: function(){
+                this.get('widthMarkers').do('updateHeight');
+                this.get('grid').do('updateDimensions');
+            }
+        },
+
+        registerEventListeners: {
+            comment: 'I register eventListeners on the window.document for DocumentMenu and CreationMenu.',
+            code: function(){
+
+                var self = this,
+                    creationMenu = this.get('creationMenu'),
+
+                    startX, 
+                    startY,
+                    dragging,
+
+                    outOfBounds,
+                    centeredPage,
+                    pseudoMargin,
+                    pageLeft,
+                    pageWidth,
+
+                    widthMarkersVisible,
+                    gridVisible,
+                    
+
+                    onMouseDown = function(evt){
+
+                        if(evt.button !== 0) return;
+
+                        if(    evt.clientX > document.documentElement.clientWidth
+                            || evt.clientY > document.documentElement.clientHeight ){
+                            // browser scrollbars
+                            return;
+                        }
+
+                        startX    = evt.pageX;
+                        startY    = evt.pageY;
+                        dragging  = false;
+
+                        centeredPage = self.get('layout').centered;
+                        pageWidth    = self.get('layout').width;
+                        if(SuperGlue.get('document').get('grid').get('active')){
+                            var gridSize = SuperGlue.get('document').get('grid').get('gridSize');
+                            pageWidth = Math.floor(pageWidth / gridSize) * gridSize;
+                        }
+                        pseudoMargin = centeredPage 
+                                        ? (parseInt(window.getComputedStyle(document.body).getPropertyValue('width')) - pageWidth) / 2
+                                        : 0;
+                        pageLeft     = pseudoMargin > 0 ? pseudoMargin : 0;
+                        pageRight    = pageLeft + pageWidth;
+                        outOfBounds  = startX < pageLeft || (centeredPage && startX > pageRight) || startY < 0;
+
+
+                        document.addEventListener('mousemove', onMouseMove, true);
+                        document.addEventListener('mouseup',   onMouseUp,   true);
+
+                        evt.preventDefault();
+                    },
+
+                    onMouseMove = function(evt){
+
+                        if(    !dragging
+                            && (    evt.pageX < startX - 10
+                                ||  evt.pageX > startX + 10
+                                ||  evt.pageY < startY - 10
+                                ||  evt.pageY > startY + 10 )
+                        ){
+                            dragging = true;
+
+                            if(!outOfBounds){
+
+                                SuperGlue.get('selection').do('clearAll')
+                                self.set({ interactionInProgress: true });
+
+                                var widthMarkers = SuperGlue.get('document').get('widthMarkers');
+                                    grid         = SuperGlue.get('document').get('grid');
+                                widthMarkersVisible = widthMarkers.get('visible');
+                                gridVisible         = grid.get('visible');
+                                widthMarkers.set({ visible: true });
+                                grid.set({ visible: true });
+                                
+                                creationMenu.do('trigger', { 
+                                    startX: startX - pageLeft,
+                                    startY: startY,
+                                    stopX:  evt.pageX - pageLeft,
+                                    stopY:  evt.pageY
+                                });
+
+                            }
+                            
+                        }
+                        if(dragging && !outOfBounds){
+
+                            if(evt.pageX <= pageLeft){
+                                creationMenu.set({
+                                    stopX: 0
+                                });
+                            }else if(evt.pageX >= pageRight){
+                                creationMenu.set({
+                                    stopX: pageWidth
+                                });
+                            }else{
+                                creationMenu.set({
+                                    stopX: (evt.pageX - pageLeft)
+                                });
+                            }
+
+                            if(evt.pageY > 0){
+                                creationMenu.set({
+                                    stopY: evt.pageY
+                                });
+                            }else{
+                                creationMenu.set({
+                                    stopY: 0
+                                });
+                            }
+                            
+
+                        }
+                    },
+
+                    onMouseUp = function(evt){
+                        
+                        if(dragging){
+
+                            self.set({ interactionInProgress: false });
+
+                            if(!outOfBounds){
+                                SuperGlue.get('document').get('widthMarkers').set({ visible: widthMarkersVisible });
+                                SuperGlue.get('document').get('grid').set({ visible: gridVisible });
+                            }
+                            
+                            if( !outOfBounds
+                                &&(evt.pageX < startX - 50
+                                || evt.pageX > startX + 50
+                                || evt.pageY < startY - 50
+                                || evt.pageY > startY + 50)
+                            ){
+                                creationMenu.do('showItems');
+                            }else{
+                                creationMenu.do('close');
+                            }
+
+
+                        }else{
+
+
+                            if(SuperGlue.get('selection').do('isEmpty')){
+
+                                if(creationMenu.get('active')){
+                                    creationMenu.do('close');
+                                }else{
+                                    self.get('documentMenu').do('trigger', { x: evt.pageX, y: evt.pageY });
+                                }
+
+                            }else{
+
+                                SuperGlue.get('selection').do('clearAll')
+
+                            }
+
+                        }
+                        
+                        document.removeEventListener('mousemove', onMouseMove, true);
+                        document.removeEventListener('mouseup',   onMouseUp,   true);
+
+                        evt.preventDefault();
+
+                    };
+
+                document.addEventListener('mousedown', onMouseDown, false);
+                
+
+            }
+        }
+
+
+    }
+
+
+}});

+ 105 - 0
src/allplatforms/classes/DocumentMenu.js

@@ -0,0 +1,105 @@
+SC.loadPackage({ 'DocumentMenu': {
+
+    comment: 'I am the DocumentMenu which is shown on click on the document background.',
+
+    sharedProperties: {
+        menuContainer: { initValue: '<div id="sg-editing-document-menu"><div id="sg-editing-document-menu-top"></div><div id="sg-editing-document-menu-left"></div></div>' },
+    },
+
+    properties: {
+        myNode:         { comment: 'I hold my DOM node.' },
+        topContainer:   { comment: 'I hold the DOM node for the topContainer.' },
+        leftContainer:  { comment: 'I hold the DOM node.for the leftContainer.' },
+        menuItemsTop:   { comment: 'I hold the MenuItems for the top container.' },
+        menuItemsLeft:  { comment: 'I hold the MenuItems for the left container.' },
+        activeMenuItem: { comment: 'I hold the active MenuItems or null.' }
+    },
+
+    methods: {
+
+    	init: { 
+    		comment: 	'I init myself. Called from Document>>init.',
+    		code: 		function(){
+
+                var myNode        = (new DOMParser()).parseFromString(this.class.get('menuContainer'), 'text/html').body.firstChild,
+                    topContainer  = myNode.firstChild,
+                    leftContainer = myNode.lastChild,
+
+                    menuItemsTop  = [],
+                    menuItemsLeft = [],
+                    menuItemsClassesTop  = [ 'MenuItemUndo', 'MenuItemRedo', 'MenuItemFileManager', 'MenuItemSave', 'MenuItemSaveAs', 'MenuItemSaveRemote', 'MenuItemOutlines' ],
+                    menuItemsClassesLeft = [ 'MenuItemCenter', 'MenuItemPaste', 'MenuItemPageTitle', 'MenuItemBackgroundColor', 'MenuItemBackgroundImg', 'MenuItemBackgroundRepeat' ],
+                    menuItem = null;
+
+
+                for(var i = 0, l = menuItemsClassesTop.length; i < l; i++){
+                    menuItem = SC.init(menuItemsClassesTop[i], this);
+                    menuItemsTop.push(menuItem);
+                    topContainer.appendChild(menuItem.get('menuContainer'));
+                }
+
+                for(var i = 0, l = menuItemsClassesLeft.length; i < l; i++){
+                    menuItem = SC.init(menuItemsClassesLeft[i], this);
+                    menuItemsLeft.push(menuItem);
+                    leftContainer.appendChild(menuItem.get('menuContainer'));
+                }
+                
+                
+                this.set({
+                    myNode:         myNode,
+                    menuItemsTop:   menuItemsTop,
+                    menuItemsLeft:  menuItemsLeft,
+                    topContainer:   topContainer,
+                    leftContainer:  leftContainer,
+                    activeMenuItem: null
+                });
+
+    		}
+    	},
+
+        trigger: {
+            comment: 'Show me at { x: anInt, y: anInt }',
+            code: function(myCoordinates){
+
+                var self = this,
+                    myNode        = this.get('myNode'),
+                    topContainer  = this.get('topContainer'),
+                    leftContainer = this.get('leftContainer');
+
+                if(myNode.parentNode === document.body){
+                    return this.do('close');
+                }
+
+                topContainer.style.top   = (myCoordinates.y - 42 > 0 ? myCoordinates.y - 42 : 2 ) + 'px';
+                topContainer.style.left  = (myCoordinates.x - 42 > 0 ? myCoordinates.x      : 42) + 'px';
+                leftContainer.style.top  = (myCoordinates.y - 42 > 0 ? myCoordinates.y      : 42) + 'px';
+                leftContainer.style.left = (myCoordinates.x - 42 > 0 ? myCoordinates.x - 42 : 2 ) + 'px';
+
+
+                document.body.insertBefore(myNode, SuperGlue.get('windowManager').get('windowsContainer'));
+
+            }
+        },
+
+        close: {
+            comment: 'Hide me!',
+            code: function(){
+
+                if(this.get('activeMenuItem') !== null){
+                    this.get('activeMenuItem').set({ isMenuItemActive: false });
+                    this.set({ activeMenuItem: null });
+                }
+
+                var myNode = this.get('myNode');
+                if(myNode.parentNode === document.body){
+                    document.body.removeChild(myNode);
+                }
+
+            }
+        }
+
+
+    }
+
+
+}});

+ 267 - 0
src/allplatforms/classes/Element.js

@@ -0,0 +1,267 @@
+SC.loadPackage({ 'Element': {
+
+    comment: '(abstract) I define the common traits of all SuperGlue elements. My method Element>>awakeFromDOM is used as a static method to recreate specific elements (instances of classes to whom I am a trait).',
+
+
+    properties: {
+
+        node:           { comment: 'I hold my container DOM node.',
+                            transform: function(aNode){
+                                this.set({ contentNode: aNode /*aNode.querySelector('.sg-element-content')*/ });
+                                return aNode;
+                            }
+                        },
+
+        contentNode:    { comment: 'I hold my content\'s DOM node.' },
+        resizeHandles:  { comment: 'I hold an instance of ResizeHandles.' },
+
+
+        top:            { transform: function(anInt){
+                                var val       = anInt > 0 ? anInt : 0,
+                                    cssVal,
+                                    grid      = SuperGlue.get('document').get('grid').get('active'),
+                                    gridSize;
+
+                                if(grid){
+                                    gridSize = SuperGlue.get('document').get('grid').get('gridSize');
+                                    val = Math.round(val / gridSize) * gridSize;
+                                } 
+
+                                cssVal = val + 'px';
+                                this.get('resizeHandles').set({ top: cssVal });
+                                this.get('node').style.top = cssVal;
+                                return val;
+                            } 
+                        },
+        left:           { transform: function(anInt){
+                                var val       = anInt > 0 ? anInt : 0,
+                                    cssVal,
+                                    grid      = SuperGlue.get('document').get('grid').get('active'),
+                                    gridSize;
+
+                                if(grid){
+                                    gridSize = SuperGlue.get('document').get('grid').get('gridSize');
+                                    val = Math.round(val / gridSize) * gridSize;
+                                } 
+
+                                cssVal = val + 'px';
+                                this.get('resizeHandles').set({ left: cssVal });
+                                this.get('node').style.left = cssVal;
+                                return val;
+                            } 
+                        },
+        width:          { transform: function(anInt){
+                                var val       = anInt > 50 ? anInt : 50,
+                                    cssVal,
+                                    grid      = SuperGlue.get('document').get('grid').get('active'),
+                                    gridSize;
+
+                                if(grid){
+                                    gridSize = SuperGlue.get('document').get('grid').get('gridSize');
+                                    val = Math.round(val / gridSize) * gridSize;
+                                    if(val < 50){
+                                        val = Math.ceil(50 / gridSize) * gridSize
+                                    }
+                                }
+
+                                var nodeStyle   = this.get('node').style,
+                                    borderWidth = this.get('borderWidth'),
+                                    padding     = this.get('padding'),
+
+                                    paddingOverflow = val - 2 * (borderWidth + padding);
+
+                                if(paddingOverflow < 0){
+                                    padding += paddingOverflow / 2;
+                                    padding = padding < 0 ? 0 : padding;
+                                    this.set({ padding: padding})
+                                }
+
+                                if(borderWidth === 0){
+                                    nodeStyle.borderWidth = '';
+                                    nodeStyle.borderStyle = '';
+                                }else{
+                                    nodeStyle.borderWidth = borderWidth + 'px';
+                                    nodeStyle.borderStyle = 'solid';
+                                    nodeStyle.borderColor = this.get('borderColor');
+                                }
+
+                                if(padding === 0){
+                                    nodeStyle.padding = '';
+                                }else{
+                                    nodeStyle.padding = padding + 'px';
+                                }
+
+                                this.get('resizeHandles').set({ width: val + 'px' });
+                                this.get('node').style.width = val - 2 * (borderWidth + padding) + 'px';
+                                return val;
+                            } 
+                        },
+        height:         { transform: function(anInt){
+                                var val       = anInt > 50 ? anInt : 50,
+                                    cssVal,
+                                    grid      = SuperGlue.get('document').get('grid').get('active'),
+                                    gridSize;
+
+                                if(grid){
+                                    gridSize = SuperGlue.get('document').get('grid').get('gridSize');
+                                    val = Math.round(val / gridSize) * gridSize;
+                                    if(val < 50){
+                                        val = Math.ceil(50 / gridSize) * gridSize
+                                    }
+                                }
+
+
+                                var nodeStyle   = this.get('node').style,
+                                    borderWidth = this.get('borderWidth'),
+                                    padding     = this.get('padding');
+
+                                if(val - 2 * (borderWidth + padding) < 0){
+                                    padding += val / 2 - (borderWidth + padding);
+                                    padding = padding < 0 ? 0 : padding;
+                                    this.set({padding:padding})
+                                }
+
+                                if(borderWidth === 0){
+                                    nodeStyle.borderWidth = '';
+                                    nodeStyle.borderStyle = '';
+                                }else{
+                                    nodeStyle.borderWidth = borderWidth + 'px';
+                                    nodeStyle.borderStyle = 'solid';
+                                    nodeStyle.borderColor = this.get('borderColor');
+                                }
+
+                                if(padding === 0){
+                                    nodeStyle.padding = '';
+                                }else{
+                                    nodeStyle.padding = padding + 'px';
+                                }
+
+                                
+
+                                this.get('resizeHandles').set({ height: val + 'px' });
+                                this.get('node').style.height = val - 2 * (borderWidth + padding) + 'px';
+                                return val;
+                            } 
+                        },
+
+
+        borderWidth:    { comment: 'I hold an integer representing borderWidth in px.'
+                        },
+
+        borderColor:    { comment: 'I hold a string representing the borderColor.',
+                          transform: function(aString){
+                                this.get('node').style.borderColor = aString;
+                                return aString;
+                          }
+                        },
+
+        padding:        { comment: 'I hold an integer representing padding in px.'
+                        },
+
+
+        contentLocked:  { comment: 'Should I prevent pointer events on the contentNode?',
+                          transform: function(aBoolean){
+                                if(aBoolean){
+                                    this.get('contentNode').classList.add('sg-editing-block-events');
+                                }else{
+                                    this.get('contentNode').classList.remove('sg-editing-block-events');
+                                }
+                                return aBoolean;
+                            }
+                        },
+
+        group:          { comment: 'Do I have a selection group (id number, not 0) or not (null).',
+                          transform: function(anIntOrNull){
+                                if(anIntOrNull){
+                                    this.get('node').setAttribute('data-superglue-group', anIntOrNull.toString());
+                                }else{
+                                    this.get('node').removeAttribute('data-superglue-group')
+                                }
+                                return anIntOrNull;
+                          }
+                        }
+
+    },
+
+    methods: {
+
+    	awakeFromDOM: { 
+    		comment: 	'(static) I am a class method to recreate specific elements from sleeping DOM nodes. Called from SuperGlue>>init.',
+    		code: 		function(aNode){
+
+                var theRevivedOne = SC.init(aNode.getAttribute('data-superglue-type'), aNode);
+                
+                return theRevivedOne;
+
+    		}
+    	},
+
+
+        init: {
+            comment:    'Shared initialization routine for all elements. Must be called by theirClass>>init via this.delegate(\'Element\', \'init\', aNode).',
+            code:       function(aNode){
+
+                this.set({ node: aNode });
+
+                this.set({ contentLocked: true });
+                this.set({ resizeHandles: SC.init('ResizeHandles', this )});
+                
+                var groupAttribute = this.get('node').getAttribute('data-superglue-group'),
+                    borderWidth    = parseInt(this.get('contentNode').style.borderWidth),
+                    padding        = parseInt(this.get('contentNode').style.padding);
+
+                this.set({
+                    borderColor:    this.get('contentNode').style.borderColor,
+                    borderWidth:    isNaN(borderWidth) ? 0 : borderWidth,
+                    padding:        isNaN(padding) ? 0 : padding
+                });
+
+                this.set({
+                    top:    parseInt(this.get('node').style.top),
+                    left:   parseInt(this.get('node').style.left),
+                    width:  (parseInt(this.get('node').style.width)  + 2 * (this.get('borderWidth') + this.get('padding'))),
+                    height: (parseInt(this.get('node').style.height) + 2 * (this.get('borderWidth') + this.get('padding'))),
+                    group:  groupAttribute ? parseInt(groupAttribute) : null
+                });
+
+                SuperGlue.get('selection').do('registerForSelection', this)
+
+            }
+
+        },
+
+        renderYourself: {
+            comment: 'I represent myself as a string to the compiler.',
+            code: function(config){
+
+                var indent = '\n' + Array(config.indent).join('\t'),
+                    thisElement;
+
+                thisElement = indent + '<div class="sg-element" data-superglue-type="'
+                                        +this.get('node').getAttribute('data-superglue-type')
+                                        +'" style="'
+                                        +this.get('node').getAttribute('style')
+                                        +'"'
+                                        +(this.get('group') 
+                                            ? (' data-superglue-group="'+ this.get('group') +'"')
+                                            : '')
+                                        +'>'
+
+                            + '\n' + (this.get('contentNode').innerHTML
+                                        .split('\n')
+                                        .filter(function(line){ return line.trim() !== '' })
+                                        .join('\n'))
+
+                            
+                            + indent + '</div>';
+
+
+                return thisElement;
+
+            }
+        }
+
+    }
+
+
+}});

+ 39 - 0
src/allplatforms/classes/EmbedElement.js

@@ -0,0 +1,39 @@
+SC.loadPackage({ 'EmbedElement': {
+
+    comment: 'I define an embed element, for including foreign resources with so-called "embed codes".',
+
+    traits: [ 'Element' ],
+
+
+    sharedProperties: {
+        protoHTML:          { initValue: '<div class="sg-element" data-superglue-type="EmbedElement" style="left: 0px; top: 0px; width: 0px; height: 0px;">'
+                                        +'\t<pre><h1>&lt;/&gt;</h1>Replace this with embedded code.</pre>'
+                                        +'</div>' },
+        applicableWidgets:  { initValue: [ 'WidgetBackgroundColor', 'WidgetBorderColor', 'WidgetBorder', 'WidgetBorderRadius', 'WidgetPadding', 'WidgetOpacity' ] },
+        creationMenuItem:   { initValue: '<div class="sg-editing-creation-menu-container"><button id="sg-editing-creation-menu-embedElement" class="sg-editing-creation-menu-button" data-tooltip="Embed HTML Code"></button></div>' }
+    },
+    
+    properties: {
+
+    },
+
+    methods: {
+
+    	init: { 
+    		comment: 	'I initalize a new EmbedElement for the DOM node given to me as argument.',
+    		code: 		function(aNode){
+
+                this.delegate('Element', 'init', aNode);
+
+                // Custom initialisation here
+
+                
+
+    		}
+    	}
+
+
+    }
+
+
+}});

+ 91 - 0
src/allplatforms/classes/FileManager.js

@@ -0,0 +1,91 @@
+SC.loadPackage({ 'FileManager': {
+
+    comment: 'I am the FileManager.',
+
+    
+    methods: {
+
+        init: { 
+            comment:    'I init the fileManager',
+            code:       function(){
+
+            }
+        },
+
+
+        open: {
+            comment: 'I open myWindow for simple file browsing.',
+            code: function(){
+
+                
+                SuperGlue.get('windowManager').do('createWindow', {
+                    class: 'FileManagerWindow',
+                    top:    100,
+                    left:   100,
+                    width:  400,
+                    height: 410,
+                    hasOKandCancelButton: false,
+                });				
+
+            
+            }
+        },
+
+
+        chooseFile: {
+            comment: 'I open myWindow as a file chooser dialog.',
+            code: function(options){
+
+
+                SuperGlue.get('windowManager').do('createWindow', {
+                    class: 'FileManagerWindow',
+                    context: 'chooseFile',
+                    top:    100,
+                    left:   100,
+                    width:  400,
+                    height: 410,
+                    hasOKandCancelButton: true,
+                    selectPath: options.oldPath,
+                    callback: function() {
+
+                        options.callback.call(this, this.path);
+
+                    }
+                });
+
+            
+            }
+        },
+
+
+        saveAs: {
+            comment: 'I open myWindow as a saveAs dialog.',
+            code: function(callback){
+
+
+                SuperGlue.get('windowManager').do('createWindow', {
+                    class: 'FileManagerWindow',
+                    context: 'saveAs',
+                    top:    100,
+                    left:   100,
+                    width:  400,
+                    height: 410,
+                    hasOKandCancelButton: true,
+                    originalFileName: document.location.pathname.split('/').slice(-1).join(),
+                    callback: function() {
+
+                        callback.call(this, this.path);
+
+                    }
+                });
+                
+            
+            }
+        }
+
+
+
+    }
+
+
+}});

+ 1143 - 0
src/allplatforms/classes/FileManagerWindow.js

@@ -0,0 +1,1143 @@
+SC.loadPackage({ 'FileManagerWindow': {
+
+    comment: 'I am a FileManagerWindow.',
+
+
+    traits: ['Window'],
+
+
+    properties: {
+        
+        fileManager:  { comment: 'I hold the fileManager to which I am the window.' },
+        
+        context: { 
+        	comment: 'I hold the context from which I was called ("","saveAs","chooseFile")',
+        	transform: function(context) {
+
+        		if ( context == 'saveAs' ) {
+        			
+        			this.get('content').querySelector('.sg-filemanager-directory-container').classList.add('nameInput');
+
+        			var self = this;
+
+        			var modalNameInput = document.createElement('input');
+        				modalNameInput.setAttribute('type', 'text');
+        				modalNameInput.classList.add('sg-modal-name-input');
+        				modalNameInput.setAttribute('value', self.get('originalFileName') );
+        				modalNameInput.addEventListener('keyup', function(evt) {
+        					if ( evt.target.value.length >= 1 ) {
+                        		self.set({ confirmPath: self.get('basePath') +'/'+ evt.target.value });
+                        		self.get('content').querySelector('button.confirm').classList.add('active');
+                        	} else {
+                        		self.get('content').querySelector('button.confirm').classList.remove('active');
+                        	}
+        				});
+        				modalNameInput.addEventListener('paste', function(evt) {
+        					if ( evt.target.value.length >= 1 ) {
+                        		self.set({ confirmPath: self.get('basePath') +'/'+ evt.target.value });
+                        		self.get('content').querySelector('button.confirm').classList.add('active');
+                        	} else {
+                        		self.get('content').querySelector('button.confirm').classList.remove('active');
+                        	}
+        				});
+
+        			this.get('content').querySelector('.sg-filemanager-directory-container').appendChild(modalNameInput);
+
+        			modalNameInput.focus();
+
+
+        		} else if ( context == 'chooseFile' ) {
+        			
+        			this.get('content').querySelector('.sg-filemanager-directory-container').classList.remove('nameInput');
+        			if ( this.get('content').querySelector('.sg-modal-name-input') ) {
+        				this.get('content').querySelector('.sg-modal-name-input').remove();
+        			}
+        			
+
+        		} else {
+        			
+        			this.get('content').querySelector('.sg-filemanager-directory-container').classList.remove('nameInput');
+        			if ( this.get('content').querySelector('.sg-modal-name-input') ) {
+        				this.get('content').querySelector('.sg-modal-name-input').remove();
+        			}
+
+        		}
+
+        		return context;
+
+        	}
+        },
+        
+        currentPath: {
+            comment: 'I hold the current file path.',
+            transform: function(path){
+                
+                var cleanPath;
+				if ( path == '/' ) {
+					cleanPath = '';
+				} else {
+					cleanPath = path;
+				}
+                this.set({ basePath: cleanPath });
+
+                this.get('directoryControlContainer').querySelector('.sg-filemanager-current-path').innerHTML = path;
+
+                return path;
+
+            } 
+        },
+        
+        originalFileName: { comment: 'I hold the original file name when the context is "saveAs".' },
+        
+        callback: { comment: 'I hold the callback when the context is "saveAs" or "chooseFile".' },
+        
+        basePath: { comment: 'I hold the current base path which strips of "/" when currentPath is the root directory.' },
+		
+		confirmPath: { comment: 'I hold the path that is given to the callback on confirmation.' },
+
+		copiedFilePath: {
+			comment: 'I hold the copied file path to use when pasteToAvailableLocation is called.',
+			transform: function(path) {
+
+				if ( path ) {
+					this.get('optionContainer').querySelector('.sg-filemanager-operation.paste').classList.add('active');
+					this.set({ copiedFileName: this.get('selectedName') });
+					this.set({ copiedFileType: this.get('selectedType') });
+				} else {
+					this.get('optionContainer').querySelector('.sg-filemanager-operation.paste').classList.remove('active');
+				}
+
+				return path;
+				
+
+			}
+		},
+
+		copiedFileName: { comment: 'I hold the copied file name to use when pasteToAvailableLocation is called.' },
+
+		copiedFileType: { comment: 'I hold the copied file type (directory/file) to use when pasteToAvailableLocation is called.' },
+        
+        selectedName: { comment: 'I hold the current file / directory name. Empty string if nothing is selected.' },
+        
+        selectedType: { comment: 'I hold the type (file or directory) of the selection.' },
+        
+        isFileSelected: { 
+            comment: 'Wether a File or Directory is selected.',
+            transform: function(aBoolean){
+
+                this.get('previewContainer').classList.remove('active');
+                this.get('previewContainer').innerHTML = '';
+
+                if (aBoolean) {
+                	
+                    var selectedFile = this.get('directoryListing').querySelector('[data-path="'+ this.get('basePath') +'/'+ this.get('selectedName') +'"]');
+                    this.get('optionContainer').querySelector('.delete').classList.add('active');
+                    this.get('optionContainer').querySelector('.rename').classList.add('active');
+                    this.get('optionContainer').querySelector('.copy').classList.add('active');
+
+                    if ( this.get('selectedType') == 'file' ) {
+
+                    	if ( this.get('hasOKandCancelButton') && this.get('context') != 'saveAs' ) {
+	                        this.get('content').querySelector('button.confirm').classList.add('active');
+	                        this.set({ confirmPath: this.get('basePath') +'/'+ this.get('selectedName') });
+	                    }
+
+	                    if ( selectedFile && selectedFile.classList.contains('image') ) {
+                            var preview = document.createElement('img');
+                            	preview.setAttribute('src', selectedFile.getAttribute('data-path'));
+                            this.get('previewContainer').classList.add('active');
+                            this.get('previewContainer').appendChild(preview);
+                        }
+
+                    } else {
+                    	
+                    	if ( this.get('hasOKandCancelButton') && this.get('context') != 'saveAs' ) {
+	                        this.get('content').querySelector('button.confirm').classList.remove('active');
+	                    }
+
+                    }
+
+                } else {
+                    
+                    this.get('optionContainer').querySelector('.delete').classList.remove('active');
+                    this.get('optionContainer').querySelector('.rename').classList.remove('active');
+                    this.get('optionContainer').querySelector('.copy').classList.remove('active');
+
+                    var selectedFiles = this.get('directoryListing').querySelectorAll('.active');
+                    for (var i = 0; i < selectedFiles.length; i++) {
+                    	selectedFiles[i].classList.remove('active');
+                    }
+
+                    if ( this.get('selectedType') == 'file' ) {
+                    	if ( this.get('hasOKandCancelButton') && this.get('context') != 'saveAs' ) {
+	                        this.get('content').querySelector('button.confirm').classList.remove('active');
+	                    }
+	                }
+
+                }
+                
+                return aBoolean;
+            }
+        },
+        
+        isWorking: { 
+            comment: 'Wether a request is currently done.',
+            transform: function(aBoolean){
+
+                if ( aBoolean ) {
+                    // working
+
+                    if ( !this.get('content').querySelector('.sg-filemanager-working') ) {
+                    	var workingIndicator = document.createElement('div');
+	                        workingIndicator.setAttribute('class', 'sg-filemanager-working');
+	                    this.get('content').appendChild(workingIndicator);
+                    }
+
+                    if ( !this.get('content').querySelector('.sg-filemanager-blocked') ) {
+                    	var blockingIndicator = document.createElement('div');
+	                        blockingIndicator.setAttribute('class', 'sg-filemanager-blocked');
+	                    this.get('content').querySelector('.sg-filemanager-directory-container').appendChild(blockingIndicator);
+                    }
+
+                    var activeButtons = this.get('content').querySelectorAll('.sg-filemanager-operation.active');
+                    for (var i = 0; i<activeButtons.length; i++) {
+                    	activeButtons[i].classList.remove('active');
+                    }
+                    
+                    if ( this.get('hasOKandCancelButton') && this.get('context') != 'saveAs' ) {
+                        this.get('content').querySelector('button.confirm').classList.remove('active');
+                    }
+
+                } else {
+                    // finished
+                    
+                    if ( this.get('content').querySelector('.sg-filemanager-working') ) {
+                    	this.get('content').querySelector('.sg-filemanager-working').remove();
+                    }
+
+                    if ( this.get('content').querySelector('.sg-filemanager-blocked') ) {
+	                    this.get('content').querySelector('.sg-filemanager-blocked').remove();
+                    }
+
+                    this.get('optionContainer').querySelector('.file-upload').classList.add('active');
+                    this.get('optionContainer').querySelector('.directory-new').classList.add('active');
+                    
+
+                }
+                
+                return aBoolean;
+            }
+        },
+        
+        hasOKandCancelButton: { 
+            comment: 'Wether I have a OK and Cancel button.',
+            transform: function(aBoolean){
+
+                if (aBoolean) {
+                    if ( !this.get('content').querySelector('sg-editing-filemanager-modal-container') ) {
+                        var self = this;
+
+                        var modalButtonContainer = document.createElement('div');
+                            modalButtonContainer.classList.add('sg-editing-filemanager-modal-container');
+
+                        var modalButtonConfirm = document.createElement('button');
+                            modalButtonConfirm.classList.add('confirm');
+                            modalButtonConfirm.addEventListener('click', function() {
+                                
+                                // Send Callback Path and Close
+                                // TODO: Close only if not opened before
+                                self.get('callback').call({ path: self.get('confirmPath') });
+                                SuperGlue.get('windowManager').do('closeWindow', self);
+
+                            });
+
+                        var modalButtonCancel = document.createElement('button');
+                            modalButtonCancel.classList.add('cancel', 'active');
+                            modalButtonCancel.addEventListener('click', function() {
+                                
+                                // Close
+                                // TODO: Close only if not opened before
+                                SuperGlue.get('windowManager').do('closeWindow', self);
+
+                            });
+
+                        modalButtonContainer.appendChild(modalButtonConfirm);
+                        modalButtonContainer.appendChild(modalButtonCancel);
+                        this.get('content').appendChild(modalButtonContainer);
+                    }
+                } else {
+                    if ( this.get('content').querySelector('sg-editing-filemanager-modal-container') ) {
+                        this.get('content').querySelector('sg-editing-filemanager-modal-container').remove();
+                    }
+                }
+                
+                return aBoolean;
+            }
+       },
+       
+       optionContainer: { comment: 'I hold a reference to the file operations panel.' },
+       
+       directoryListing: { comment: 'I hold a reference to the directory container.' },
+       
+       directoryControlContainer: { comment: 'I hold a reference to the directory controls.' },
+       
+       previewContainer: { comment: 'I hold a reference to the image preview container.' }
+
+    },
+
+    methods: {
+
+        init: { 
+            comment:    'I start a new FileManagerWindow. My argument is '+
+                        '{ top: anInt, left: anInt, width: anInt, height: anInt }.',
+            code:       function(startConfig){
+                
+                this.delegate('Window', 'init', startConfig);
+                this.set({ hasOKandCancelButton: startConfig.hasOKandCancelButton });
+                if ( startConfig.context == 'saveAs' ) {
+                	this.set({ originalFileName: startConfig.originalFileName });
+                }
+
+                if ( startConfig.callback ) {
+                	this.set({ callback: startConfig.callback });
+                }
+
+                var self = this;
+
+                // Directory Listing
+
+                var directoryContainerElement = document.createElement('div');
+                    directoryContainerElement.setAttribute('class', 'sg-filemanager-directory-container');
+
+                var directoryListingElement = document.createElement('ul');
+                    directoryListingElement.setAttribute('class', 'sg-filemanager-directory-listing');
+                    directoryListingElement.addEventListener('mouseup', function(evt) {
+                    	self.set({ isFileSelected: false });
+                    	evt.stopPropagation();
+                    });
+
+                self.set({ directoryListing: directoryListingElement });
+
+                directoryContainerElement.appendChild(directoryListingElement);
+                self.get('content').appendChild(directoryContainerElement);
+
+                if ( startConfig.selectPath ) {
+                	var fullPath = startConfig.selectPath;
+                	var fullPathArray = fullPath.split('/');
+                		fullPathArray.pop();
+                	var parentDirectory = fullPathArray.join('/');
+                	self.do('listDirectory', { path: parentDirectory, selectPath: startConfig.selectPath });
+                } else {
+                	self.do('listDirectory', { path: '/' });
+                }
+                
+
+                // Directory Controls
+
+                var directoryControls = document.createElement('div');
+                    directoryControls.setAttribute('class', 'sg-filemanager-directory-controls');
+
+                self.get('content').appendChild(directoryControls);
+                self.set({ directoryControlContainer: directoryControls });
+
+                // Go UP one directory button
+                var optionDirectoryUp = document.createElement('div');
+                    optionDirectoryUp.classList.add('sg-filemanager-operation', 'directory-up');
+                    optionDirectoryUp.addEventListener('click', function() {
+                        
+                        if ( self.get('currentPath') == '/' ) {
+                            return false;
+                        }
+
+                        var path = self.get('currentPath');
+                        var pathArray = path.split('/');
+                        pathArray.pop();
+                        
+                        var newPath;
+                        if (pathArray.length > 1 ) {
+                            newPath = pathArray.join('/');
+                        } else if (pathArray.length == 1) {
+                            newPath = '/';
+                        }
+                        
+                        self.do('listDirectory', { path: newPath });
+                    });
+
+                directoryControls.appendChild(optionDirectoryUp);
+
+                // Current path display
+                var currentPathContainer = document.createElement('div');
+                    currentPathContainer.classList.add('sg-filemanager-current-path');
+
+                directoryControls.appendChild(currentPathContainer);
+
+                // File Operations
+                
+                var optionContainerElement = document.createElement('div');
+                    optionContainerElement.setAttribute('class', 'sg-filemanager-operations');
+
+                self.set({ optionContainer: optionContainerElement });
+                self.get('content').appendChild(self.get('optionContainer'));
+
+                // Upload file button
+                var optionUploadFile = document.createElement('div');
+                    optionUploadFile.classList.add('sg-filemanager-operation', 'file-upload', 'active');
+                    optionUploadFile.addEventListener('click', function(evt) {                                
+                        
+                        self.set({ isFileSelected: false });
+
+                        var fileInput = document.createElement('input');
+                            fileInput.setAttribute('type', 'file');
+                            fileInput.style.visibility = 'hidden';
+                        self.get('optionContainer').appendChild(fileInput);
+
+                        fileInput.addEventListener('change', function(){
+
+                        	self.do('uploadWithAvailableFilename', { name: fileInput.files[0].name, data: fileInput.files[0] });	
+
+                        });
+                        
+                        fileInput.click();
+                        
+
+                    });
+                
+                // Add new directory button (in current directory)
+                var optionNewDirectory = document.createElement('div');
+                    optionNewDirectory.classList.add('sg-filemanager-operation', 'directory-new', 'active');
+                    optionNewDirectory.addEventListener('click', function() {
+                        
+                        self.get('directoryListing').scrollTop = 0;
+                        
+                        var selectedFiles = self.get('directoryListing').querySelectorAll('.active');
+                        for (var i = 0; i < selectedFiles.length; i++) {
+                        	selectedFiles[i].classList.remove('active');
+                        }
+
+                        var directoryNameInputContainer = document.createElement('li');
+                        	directoryNameInputContainer.classList.add('sg-resource-directory', 'new');
+
+                        var alreadyFired;
+                        var directoryNameInput = document.createElement('input');
+                        	directoryNameInput.setAttribute('type', 'text');
+                        	directoryNameInput.setAttribute('value', 'NewFolder');
+                        	directoryNameInput.addEventListener('blur', function(evt) {
+                        		setDirectoryName(evt);
+                        		alreadyFired = true;
+                        	});
+                        	directoryNameInput.addEventListener('keydown', function(evt) {
+                        		if ( evt.keyCode == 13 ) {
+                        			setDirectoryName(evt);
+                        			alreadyFired = true;
+                        		}
+                        	});
+
+                        directoryNameInputContainer.appendChild(directoryNameInput);
+                        self.get('directoryListing').insertBefore(directoryNameInputContainer, self.get('directoryListing').firstChild);
+
+                        directoryNameInput.focus();
+                        directoryNameInput.select();
+                        
+                        var setDirectoryName = function(evt) {
+                        	
+                        	if (evt.target.value != '' && !alreadyFired) {
+                            	
+	                            self.set({ isWorking: true });
+	                            
+	                            var path;
+	                            if (self.get('currentPath') == '/' ) {
+		                            path = '';
+		                        } else {
+		                            path = self.get('currentPath');
+		                        }
+		                        
+		                        self.do('createAvailableDirectory', { name: evt.target.value });
+
+	                        }
+
+                        }
+                        
+                    });
+                
+				// Rename button (file or folder)
+                var optionRename = document.createElement('div');
+                    optionRename.classList.add('sg-filemanager-operation', 'rename');
+                    optionRename.addEventListener('click', function(evt) {
+                        
+                        if ( !this.classList.contains('active') ) {
+                            return false;
+                        }
+
+                        // TODO: Scroll to resource position
+                        //self.get('directoryListing').scrollTop = 0;
+                        
+                        var selectedFiles = self.get('directoryListing').querySelectorAll('.active');
+                        for (var i = 0; i < selectedFiles.length; i++) {
+                        	selectedFiles[i].classList.remove('active');
+                        }
+
+                        var oldName = self.get('selectedName');
+
+                        var selectedElement = self.get('directoryListing').querySelector( '[data-path="'+ self.get('basePath') +'/'+ oldName +'"]' );
+
+                        selectedElement.classList.add('edit');
+
+                        var alreadyFired;
+                        var renameInput = document.createElement('input');
+                        	renameInput.setAttribute('type', 'text');
+                        	renameInput.setAttribute('value', oldName);
+                        	renameInput.addEventListener('blur', function(evt) {
+                        		setNewName(evt);
+                        		alreadyFired = true;
+                        	});
+                        	renameInput.addEventListener('keydown', function(evt) {
+                        		if ( evt.keyCode == 13 ) {
+                        			setNewName(evt);
+                        			alreadyFired = true;
+                        		}
+                        	});
+
+                        selectedElement.appendChild(renameInput);
+
+                        renameInput.focus();
+                        renameInput.select();
+                        
+                        var setNewName = function(evt) {
+                        	
+                        	if (evt.target.value != '' && !alreadyFired) {
+                            	
+	                            self.set({ isWorking: true });
+		                        self.do('renameToAvailableFilename', { name: evt.target.value, origin: self.get('basePath') +'/'+ oldName, type: self.get('selectedType') });
+
+	                        }
+
+                        }
+                        
+                    });
+
+				// Copy button (file or folder)
+                var optionCopy = document.createElement('div');
+                    optionCopy.classList.add('sg-filemanager-operation', 'copy');
+                    optionCopy.addEventListener('click', function(evt) {
+                        
+                        if ( !this.classList.contains('active') ) {
+                            return false;
+                        }
+
+                        self.set({ copiedFilePath: self.get('basePath') +'/'+ self.get('selectedName') });
+                        
+                    });
+
+                // Paste button (file or folder)
+                var optionPaste = document.createElement('div');
+                    optionPaste.classList.add('sg-filemanager-operation', 'paste');
+                    optionPaste.addEventListener('click', function(evt) {
+                        
+                        if ( !this.classList.contains('active') || !self.get('copiedFilePath') ) {
+                            return false;
+                        }
+                        
+                        var selectedFiles = self.get('directoryListing').querySelectorAll('.active');
+                        for (var i = 0; i < selectedFiles.length; i++) {
+                        	selectedFiles[i].classList.remove('active');
+                        }
+
+                        self.set({ isWorking: true });
+		                self.do('pasteToAvailableLocation', { originPath: self.get('copiedFilePath') });
+                        
+                    });
+
+				// Delete button (file or folder)
+                var optionDelete = document.createElement('div');
+                    optionDelete.classList.add('sg-filemanager-operation', 'delete');
+                    optionDelete.addEventListener('click', function(evt) {
+                        
+                        if ( !this.classList.contains('active') ) {
+                            return false;
+                        }
+                      	
+                        var fullPath = self.get('basePath') +'/'+ self.get('selectedName');
+
+                        if ( self.get('selectedType') == 'file' ) {
+                        	var deleteFileConfirmation = confirm('Are you sure you want to delete the file:\n\n ' + fullPath + ' ?\n\nThis action is irreversible!');
+                        
+	                        if ( deleteFileConfirmation == true ) {
+
+	                            self.set({ isWorking: true });
+
+	                            SuperGlue.get('server').do('removeFile', {
+	                                path: fullPath,
+	                                onerror: function(){ console.log(arguments) },
+	                                onsuccess: function() {
+	                                    
+	                                    self.set({ isWorking: false });
+	                                    self.do('listDirectory', { path: self.get('currentPath') });
+
+	                                }
+	                            });
+
+	                        }
+                        } else {
+                        	var deleteDirectoryConfirmation = confirm('Are you sure you want to delete the folder:\n\n ' + fullPath + ' \n\nand all of its contents?\nThis action is irreversible!');
+                        
+	                        if ( deleteDirectoryConfirmation == true ) {
+
+	                            self.set({ isWorking: true });
+
+	                            SuperGlue.get('server').do('removeDirectory', {
+	                                path: fullPath,
+	                                onerror: function(){ console.log(arguments) },
+	                                onsuccess: function() {
+	                                    
+	                                    self.set({ isWorking: false });
+	                                    self.do('listDirectory', { path: self.get('currentPath') });
+
+	                                }
+	                            });
+
+	                        }
+                        }
+                        
+                    });
+
+                optionContainerElement.appendChild(optionUploadFile);
+                optionContainerElement.appendChild(optionNewDirectory);
+                optionContainerElement.appendChild(optionRename);
+                optionContainerElement.appendChild(optionCopy);
+                optionContainerElement.appendChild(optionPaste);
+                optionContainerElement.appendChild(optionDelete);
+
+
+                var previewContainer = document.createElement('div');
+                	previewContainer.classList.add('sg-filemanager-preview');
+
+                self.get('content').appendChild(previewContainer);
+                self.set({ previewContainer: previewContainer });
+
+                self.set({ context: startConfig.context });
+
+
+            }
+        },
+
+        listDirectory: {
+            comment:    'I list a directory by a given path.',
+            code:       function(arg) {
+                
+                var self = this;
+
+                self.set({ isWorking: true });
+
+                SuperGlue.get('server').do('directoryListing', {
+                    path: arg.path,
+                    onerror: function(){ console.log(this) },
+                    onsuccess: function() { 
+
+                        self.get('directoryListing').innerHTML = '';
+
+                        var result = this;
+
+                        /*
+                        result.sort(function(a, b){
+                            var keyA = a.isDirectory,
+                                keyB = b.isDirectory;
+                            if(keyA < keyB) return 1;
+                            if(keyA > keyB) return -1;
+                            return 0;
+                        });
+
+						*/
+						var directories = [],
+							files = [];
+
+						for (var r=0; r < result.length; r++) {
+							if ( result[r].isDirectory ) {
+								directories.push(result[r]);
+							} else {
+								files.push(result[r]);
+							}
+						}
+						
+                        for (var d=0; d < directories.length; d++) {
+
+                            var pathArray = directories[d].name.split('/');
+                            var name = pathArray[pathArray.length-1];
+                            var type = 'directory';
+                            
+                            var resultElem = document.createElement('li');
+	                            resultElem.setAttribute('class', 'sg-resource-'+ type);
+	                            resultElem.setAttribute('data-path', directories[d].name);
+	                            resultElem.setAttribute('data-name', name);
+	                            resultElem.setAttribute('data-type', type);
+	                            resultElem.innerHTML = name;
+
+	                            resultElem.addEventListener('click', function(evt) {
+	                                
+	                                var selectedFiles = self.get('directoryListing').querySelectorAll('.active');
+	                                for (var i = 0; i < selectedFiles.length; i++) {
+	                                	selectedFiles[i].classList.remove('active');
+	                                }
+	                                
+	                                self.get('previewContainer').classList.remove('active');
+	                                self.get('previewContainer').innerHTML = '';
+
+	                                this.classList.add('active');
+
+	                                self.set({ selectedName: this.getAttribute('data-name') });
+	                                self.set({ selectedType: this.getAttribute('data-type') });
+	                                self.set({ isFileSelected: true });
+
+	                            });
+
+	                            resultElem.addEventListener('dblclick', function(evt) {
+	                                self.do('listDirectory', { path: evt.target.getAttribute('data-path') });
+
+	                            });
+
+	                        self.get('directoryListing').appendChild(resultElem);
+                        }
+
+                        function bytesToSize(bytes) {
+						   if(bytes == 0) return '0 Byte';
+						   var k = 1000;
+						   var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
+						   var i = Math.floor(Math.log(bytes) / Math.log(k));
+						   return (bytes / Math.pow(k, i)).toPrecision(3) + ' ' + sizes[i];
+						}
+
+                        for (var f=0; f < files.length; f++) {
+
+							var pathArray = files[f].name.split('/');
+                            var name = pathArray[pathArray.length-1];
+                        	var type = 'file';
+                        	var size = bytesToSize(files[f].size);
+                                
+                            var fileType;
+                            if ( (/\.(gif|jpg|jpeg|tiff|png)$/i).test(files[f].name) ) {
+                                fileType = 'image';
+                            } else {
+                            	fileType = undefined;
+                            }
+
+                            var resultElem = document.createElement('li');
+	                            resultElem.classList.add('sg-resource-'+ type);
+	                            
+	                            if (fileType) {
+	                            	resultElem.classList.add(fileType);
+	                            }
+	                            resultElem.setAttribute('data-path', files[f].name);
+	                            resultElem.setAttribute('data-name', name);
+	                            resultElem.setAttribute('data-type', type);
+	                            resultElem.innerHTML = name + '<span class="size">'+ size +'</span>';
+
+	                            resultElem.addEventListener('click', function(evt) {
+	                                
+	                                var selectedFiles = self.get('directoryListing').querySelectorAll('.active');
+	                                for (var i = 0; i < selectedFiles.length; i++) {
+	                                	selectedFiles[i].classList.remove('active');
+	                                }
+
+	                                this.classList.add('active');
+	                                
+	                                self.set({ selectedName: this.getAttribute('data-name') });
+	                                self.set({ selectedType: this.getAttribute('data-type') });
+	                                self.set({ isFileSelected: true });
+
+	                            });
+
+	                        self.get('directoryListing').appendChild(resultElem);    
+                        }
+
+                        if ( arg.path == '/' ) {
+                            self.get('directoryControlContainer').querySelector('.directory-up').classList.remove('active');
+                            self.get('optionContainer').querySelector('.delete').classList.remove('active');
+                        } else {
+                            self.get('directoryControlContainer').querySelector('.directory-up').classList.add('active');
+                        }
+                        
+                        self.set({ currentPath: arg.path });
+
+                        if ( self.get('context') == 'saveAs' && self.get('content').querySelector('.sg-modal-name-input') ) {
+                        	self.set({ confirmPath: self.get('basePath') +'/'+ self.get('content').querySelector('.sg-modal-name-input').value });
+                        }
+
+                        if ( arg.selectPath && self.get('directoryListing').querySelector('[data-path="'+ arg.selectPath +'"]') ) {
+                        	
+                        	var selectedFiles = self.get('directoryListing').querySelectorAll('.active');
+                            for (var i = 0; i < selectedFiles.length; i++) {
+                            	selectedFiles[i].classList.remove('active');
+                            }
+                        	
+                        	self.get('directoryListing').querySelector('[data-path="'+ arg.selectPath +'"]').classList.add('active');
+                        	
+                        	self.set({ selectedType: self.get('directoryListing').querySelector('[data-path="'+ arg.selectPath +'"]').getAttribute('data-type') });
+                        	self.set({ selectedName: self.get('directoryListing').querySelector('[data-path="'+ arg.selectPath +'"]').getAttribute('data-name') });
+                        	self.set({ isFileSelected: true });
+
+                        } else {
+                        	
+                        	self.set({ isFileSelected: false });
+
+                        }
+                        
+
+                        self.set({ isWorking: false });
+
+                        if ( self.get('copiedFilePath') ) {
+                        	self.get('optionContainer').querySelector('.paste').classList.add('active');
+                        }
+                        
+                    }
+                });
+
+            }
+        },
+
+        checkName: {
+            comment:    'I check if the name is valid and if not return a valid name. Params: name',
+            code:       function(arg) {
+
+                // Replace all illegal characters with '-'
+                var cleanString = arg.name.replace(/ |\\|%|"|'|<|>|\/|\.\.|\$|&|\{|\}|\[|\]|#|\?|,/gi, '-');
+
+                // Replace Umlauts
+                cleanString.replace(/ä/gi, 'ae').replace(/ö/gi, 'oe').replace(/ü/gi, 'ue');
+
+                return cleanString;
+
+            }
+        },
+
+        createAvailableDirectory: {
+            comment:    'I create a directrory with a valid and available name. Params: name, (increment)',
+            code:       function(arg) {
+
+                var self = this;
+
+                var increment;
+                if (!arg.increment) {
+                	increment = 1;
+                } else {
+                	increment = parseInt(arg.increment)+1;
+                }
+                
+                var cleanedName = self.do('checkName', { name: arg.name });
+                
+                var destination;
+        		if (increment == 1) {
+        			destination = self.get('basePath') +'/'+ cleanedName;
+        		} else {
+        			destination = self.get('basePath') +'/'+ cleanedName + '-' + increment;
+        		}
+            	SuperGlue.get('server').do('doesDirectoryExist', {
+                    path: destination,
+                    onerror: function(){ console.log(this) },
+                    onsuccess: function(aBoolean) {
+                    	
+                    	if (aBoolean) {
+                    		var newIncrement = increment++;
+	                    	self.do('createAvailableDirectory', { name: cleanedName, increment: newIncrement });
+                    	} else {
+
+                    		var destination;
+                    		if (increment == 1) {
+                    			destination = self.get('basePath') +'/'+ cleanedName;
+                    		} else {
+                    			destination = self.get('basePath') +'/'+ cleanedName + '-' + increment;
+                    		}
+
+                            SuperGlue.get('server').do('makeDirectory', {
+                                path: destination,
+                                onerror: function(){ console.log(arguments) },
+                                onsuccess: function() {
+                                    
+                                    self.set({ isWorking: false });
+                                    self.do('listDirectory', { path: self.get('currentPath'), selectPath: destination });
+
+                                }
+                            });
+							
+                    	}
+                    	
+
+                    }
+                });
+
+            }
+        },
+
+        uploadWithAvailableFilename: {
+            comment:    'I upload a file to an available location. Params: name, data, (increment)',
+            code:       function(arg) {
+
+                var self = this;
+
+                self.set({ isWorking: true });
+
+                var increment;
+                if (!arg.increment) {
+                	increment = 1;
+                } else {
+                	increment = parseInt(arg.increment)+1;
+                }
+                
+                var cleanedName = self.do('checkName', { name: arg.name });
+                
+                var destination;
+                if (increment == 1) {
+                	destination = self.get('basePath') +'/'+ cleanedName;
+                } else {
+                	destination = self.get('basePath') + '/' + cleanedName.substr(0, (cleanedName.lastIndexOf('.')) || cleanedName) + '-' + increment + cleanedName.substring(cleanedName.lastIndexOf('.'))
+                }
+
+            	SuperGlue.get('server').do('doesFileExist', {
+                    path: destination,
+                    onerror: function(){ console.log(this) },
+                    onsuccess: function(aBoolean) {
+                    	
+                    	if (aBoolean) {
+
+                    		var newIncrement = increment++;
+	                    	self.do('uploadWithAvailableFilename', { name: cleanedName, data: arg.data, increment: newIncrement });
+                    	
+                    	} else {
+                    		
+                    		var destination;
+                    		if (increment == 1) {
+                    			destination = self.get('basePath') +'/'+ cleanedName;
+                    		} else {
+                    			destination = self.get('basePath') +'/'+ cleanedName.substr(0, (cleanedName.lastIndexOf('.')) || cleanedName) + '-' + increment + cleanedName.substring(cleanedName.lastIndexOf('.'));
+                    		}
+
+                            var progressBar = document.createElement('div');
+                                progressBar.setAttribute('class', 'sg-filemanager-progress-bar');
+
+                            var uploadProgress = document.createElement('div');
+                                uploadProgress.setAttribute('class', 'sg-filemanager-progress');
+                                uploadProgress.style.opacity = 1;
+
+                            progressBar.appendChild(uploadProgress);
+                            self.get('content').appendChild(progressBar);
+                            
+                            var file        = arg.data;
+                            var uploadForm  = new FormData();
+
+                            uploadForm.append('userimage', file);
+
+                            SuperGlue.get('server').do('upload', {
+                                data:   uploadForm,
+                                path:   destination,
+                                onerror: function() { },
+                                onprogress: function(evt){
+
+                                    if(evt.lengthComputable){
+                                        var percentComplete = evt.loaded / evt.total * 100;
+                                        uploadProgress.style.width = percentComplete + '%';
+                                    } else {
+                                        uploadProgress.style.width = 40 + '%';
+                                    }
+                                },
+                                onresponse:   function(){
+
+                               		self.get('optionContainer').querySelector('input[type="file"]').remove();
+                                    self.do('listDirectory', { path: self.get('currentPath'), selectPath: destination });
+                                    
+                                    uploadProgress.style.width = 100 + '%';
+
+                                    setTimeout(function() {
+                                    	uploadProgress.style.opacity = 0;
+                                    	self.set({ isWorking: false });
+                                    	setTimeout(function() {
+                                    		self.get('content').querySelector('.sg-filemanager-progress-bar').remove();
+                                    	}, 500);
+                                    }, 1200);
+
+                                }
+                            });
+							
+                    	}
+                    	
+
+                    }
+                });
+
+            }
+        },
+
+        renameToAvailableFilename: {
+            comment:    'I rename a file to an available and valid name. Params: name, origin, type, (increment)',
+            code:       function(arg) {
+
+                var self = this;
+
+                self.set({ isWorking: true });
+
+                var increment;
+                if (!arg.increment) {
+                	increment = 1;
+                } else {
+                	increment = parseInt(arg.increment)+1;
+                }
+                
+                var cleanedName = self.do('checkName', { name: arg.name });
+                
+                var destination;
+        		if (increment == 1) {
+        			destination = self.get('basePath') +'/'+ cleanedName;
+        		} else {
+        			if ( arg.type == 'directory' ) {
+        				destination = self.get('basePath') +'/'+ cleanedName + '-' + increment;
+        			} else {
+        				destination = self.get('basePath') +'/'+ cleanedName.substr(0, (cleanedName.lastIndexOf('.')) || cleanedName) + '-' + increment + cleanedName.substring(cleanedName.lastIndexOf('.'));
+        			}
+        		}
+
+                var checkFunctionName;
+                if (arg.type == 'directory') {
+                	checkFunctionName = 'doesDirectoryExist';
+                } else {
+                	checkFunctionName = 'doesFileExist';
+                }
+
+            	SuperGlue.get('server').do(checkFunctionName, {
+                    path: destination,
+                    onerror: function(){ console.log(this) },
+                    onsuccess: function(aBoolean) {
+
+                    	if (aBoolean /*&& checkPath +'/'+ cleanedName != arg.origin*/) {
+                    		var newIncrement = increment++;
+	                    	self.do('renameToAvailableFilename', { name: cleanedName, origin: arg.origin, type: arg.type, increment: newIncrement });
+                    	
+                    	} else {
+                    		
+                    		var destination;
+                    		if (increment == 1) {
+                    			destination = self.get('basePath') +'/'+ cleanedName;
+                    		} else {
+                    			if ( arg.type == 'directory' ) {
+                    				destination = self.get('basePath') +'/'+ cleanedName + '-' + increment;
+                    			} else {
+                    				destination = self.get('basePath') +'/'+ cleanedName.substr(0, (cleanedName.lastIndexOf('.')) || cleanedName) + '-' + increment + cleanedName.substring(cleanedName.lastIndexOf('.'));
+                    			}
+                    		}
+                            
+                            SuperGlue.get('server').do('moveFile', {
+                                sourcePath:   arg.origin,
+                                targetPath:   destination,
+                                onerror: function() { },
+                                onprogress: function(evt){
+                                    //
+                                },
+                                onsuccess:   function(){
+
+                                    self.do('listDirectory', { path: self.get('currentPath'), selectPath: destination });                                    
+                                    self.set({ isWorking: false });
+
+                                }
+                            });
+							
+                    	}
+                    	
+
+                    }
+                });
+
+            }
+        },
+
+        pasteToAvailableLocation: {
+            comment:    'I paste a file to an available and valid location in the current directory. Params: originPath, (increment)',
+            code:       function(arg) {
+
+                var self = this;
+
+                self.set({ isWorking: true });
+
+                var increment;
+                if (!arg.increment) {
+                	increment = 1;
+                } else {
+                	increment = parseInt(arg.increment)+1;
+                }
+                
+                var cleanedName = self.get('copiedFileName');
+                
+                var destination;
+        		if (increment == 1) {
+        			destination = self.get('basePath') +'/'+ cleanedName;
+        		} else {
+        			if ( arg.type == 'directory' ) {
+        				destination = self.get('basePath') +'/'+ cleanedName + '-' + increment;
+        			} else {
+        				destination = self.get('basePath') +'/'+ cleanedName.substr(0, (cleanedName.lastIndexOf('.')) || cleanedName) + '-' + increment + cleanedName.substring(cleanedName.lastIndexOf('.'));
+        			}
+        		}
+
+                var checkFunctionName;
+                if (self.get('copiedFileType') == 'directory') {
+                	checkFunctionName = 'doesDirectoryExist';
+                } else {
+                	checkFunctionName = 'doesFileExist';
+                }
+
+            	SuperGlue.get('server').do(checkFunctionName, {
+                    path: destination,
+                    onerror: function(){ console.log(this) },
+                    onsuccess: function(aBoolean) {
+
+                    	if (aBoolean /*&& checkPath +'/'+ cleanedName != arg.origin*/) {
+                    		var newIncrement = increment++;
+	                    	self.do('pasteToAvailableLocation', { originPath: arg.originPath, increment: newIncrement });
+                    	
+                    	} else {
+                    		
+                    		var destination;
+                    		if (increment == 1) {
+                    			destination = self.get('basePath') +'/'+ cleanedName;
+                    		} else {
+                    			if ( arg.type == 'directory' ) {
+                    				destination = self.get('basePath') +'/'+ cleanedName + '-' + increment;
+                    			} else {
+                    				destination = self.get('basePath') +'/'+ cleanedName.substr(0, (cleanedName.lastIndexOf('.')) || cleanedName) + '-' + increment + cleanedName.substring(cleanedName.lastIndexOf('.'));
+                    			}
+                    		}
+                            
+                            
+                            var copyFunctionName;
+			                if (self.get('copiedFileType') == 'directory') {
+			                	copyFunctionName = 'copyDirectory';
+			                } else {
+			                	copyFunctionName = 'copyFile';
+			                }
+
+                            SuperGlue.get('server').do(copyFunctionName, {
+                                sourcePath:   arg.originPath,
+                                targetPath:   destination,
+                                onerror: function() { },
+                                onprogress: function(evt){
+                                    //
+                                },
+                                onsuccess:   function(){
+
+                                    self.do('listDirectory', { path: self.get('currentPath'), selectPath: destination });                                    
+                                    self.set({ isWorking: false });
+                                    self.set({ copiedFilePath: undefined });
+
+                                }
+                            });
+							
+                    	}
+                    	
+
+                    }
+                });
+
+            }
+        }
+
+
+
+    }
+
+
+}});

+ 298 - 0
src/allplatforms/classes/Grid.js

@@ -0,0 +1,298 @@
+SC.loadPackage({ 'Grid': {
+
+    comment: 'I provide a grid for the page, with controls to set the configuration of the grid. When I am active, I arrange Elements with my method Grid>>arrangeElement.',
+
+    properties: {
+
+        gridContainer: { comment: 'I hold the DOM container in which the grid is shown as background image. ' },
+
+        gridControl:   { comment: 'I hold the DOM container for the grid controls.' },
+
+        myDocument:    { comment: 'I store a reference to my document.' },
+
+        active:     { comment: 'Wether the grid is active.',
+                      transform: function(aBoolean){
+
+                            var gridContainer = this.get('gridContainer');
+
+                            if(aBoolean){
+                                if(gridContainer.parentNode !== document.body){
+                                    document.body.insertBefore(gridContainer, this.get('myDocument').get('pageContainer'));
+                                }
+                            }else{
+                                if(gridContainer.parentNode === document.body){
+                                    document.body.removeChild(gridContainer);
+                                }
+                            }
+                            
+                            return aBoolean;
+
+                      }
+                    },
+
+        gridSize:   { comment: 'The grid size in pixel.',
+                      transform: function(anInt){
+
+                            var canvas, ctx;
+
+                            if(anInt >= 7){
+                                
+                                canvas = document.createElement('canvas');
+                                canvas.width  = anInt;
+                                canvas.height = anInt;
+                                ctx = canvas.getContext('2d');
+
+                                ctx.strokeStyle = 'rgba(255,41,61,0.4)';
+
+                                ctx.beginPath();
+                                ctx.moveTo(anInt, 0);
+                                ctx.lineTo(anInt, anInt);
+                                ctx.stroke();
+
+                                ctx.beginPath();
+                                ctx.moveTo(0, anInt);
+                                ctx.lineTo(anInt, anInt);
+                                ctx.stroke();
+
+                                this.get('gridContainer').style.backgroundImage  = 'url("' + canvas.toDataURL() + '")';
+
+                            }
+                            return anInt < 7 ? 0 : anInt;
+                      }
+                    },
+
+        visible:    { comment: 'Wether I am visible or not.',
+                      transform: function(aBoolean){
+                            if(aBoolean){
+                                this.get('gridContainer').style.display = 'block';
+                                this.get('gridControl').classList.remove('sg-editing-grid-control-hidden');
+                            }else{
+                                this.get('gridContainer').style.display = 'none';
+                                this.get('gridControl').classList.add('sg-editing-grid-control-hidden');
+                            }
+                            return aBoolean;
+                      }
+                    }
+        
+
+    },
+    
+
+    methods: {
+
+    	init: { 
+    		comment: 	'I initalize myself.',
+    		code: 		function(theDocument){
+
+                this.set({ myDocument: theDocument });
+
+                var self = this,
+                    pageContainer = theDocument.get('pageContainer'),
+                    config        = pageContainer.getAttribute('data-superglue-grid').split('/'),
+                    active        = config[0] === 'on',
+                    gridSize      = active ? parseInt(config[1]) : 0,
+
+                    gridContainer = this.set({ gridContainer: (function(){
+                                                        var gridContainer = document.createElement('div');
+                                                        gridContainer.setAttribute('id', 'sg-editing-grid-container');
+                                                        return gridContainer;
+                                                    }).call(this)
+                                    }),
+
+                    gridControl   = this.set({ gridControl: (function(){
+                                                        var gridControl = document.createElement('div');
+                                                        gridControl.setAttribute('id', 'sg-editing-grid-control');
+                                                        theDocument.get('editingContainer').appendChild(gridControl);
+                                                        return gridControl;
+                                                    }).call(this)
+                                    });
+
+
+                window.addEventListener('resize', function(){
+                    self.do('updateDimensions');
+                }, false);
+
+                this.set({
+                    active:   active,
+                    gridSize: gridSize
+                });
+
+
+                this.do('initGridControl');
+
+
+    		}
+    	},
+
+
+        updateDimensions: {
+            comment:    'I update the gridContainer\'s dimensions and position.',
+            code:       function(newConfig){
+
+                var gridContainer = this.get('gridContainer');
+
+                var layout        = newConfig ? newConfig : SuperGlue.get('document').get('layout'),
+                    minHeight     = SuperGlue.get('document') ? SuperGlue.get('document').do('getMinHeight') : 0,
+                    minWidth;
+
+
+                if(layout.centered){
+
+                    if(window.innerWidth >= layout.width){
+                        gridContainer.style.width      = layout.width + 'px';
+                        gridContainer.style.left       = '50%';
+                        gridContainer.style.marginLeft = layout.width / -2 + 'px';
+                    }else{
+                        gridContainer.style.width      = layout.width + 'px';
+                        gridContainer.style.left       = '0px';
+                        gridContainer.style.marginLeft = '0px';
+                    }
+
+                }else{
+
+                    minWidth  = SuperGlue.get('document') ? SuperGlue.get('document').do('getMinWidth') : 0;
+                    
+                    gridContainer.style.width      = (minWidth < window.innerWidth - 20 ? window.innerWidth - 20 : minWidth) + 'px';
+                    gridContainer.style.left       = '0px';
+                    gridContainer.style.marginLeft = '0px';
+
+                }
+
+                gridContainer.style.height = (minHeight < window.innerHeight ? window.innerHeight : minHeight) + 'px';
+
+
+            }
+        },
+
+        initGridControl: {
+            comment: 'I init the gridControl.',
+            code: function(){
+
+                var self = this,
+                    startX,
+                    gridSize,
+                    wasVisible,
+                    gridControl = this.get('gridControl'),
+
+                    allElements,
+                    allElementDimensions,
+                    pageWidth,
+
+                    onMouseDown = function(evt){
+
+                        startX   = evt.pageX;
+                        gridSize = self.get('gridSize');
+
+                        SuperGlue.get('document').set({ interactionInProgress: true });
+                        SuperGlue.get('selection').do('clearAll');
+
+                        wasVisible = self.get('visible');
+                        self.set({ visible: true });
+
+
+                        allElements = SuperGlue.get('document').get('children');
+                        allElementDimensions = [];
+                        for(var i = 0, l = allElements.length; i < l; i++){
+                            allElementDimensions.push({
+                                top:    allElements[i].get('top'),
+                                left:   allElements[i].get('left'),
+                                width:  allElements[i].get('width'),
+                                height: allElements[i].get('height')
+                            })
+                        }
+                        pageWidth = self.get('myDocument').get('layout').width;
+
+
+                        document.addEventListener('mousemove', onMouseMove, true);
+                        document.addEventListener('mouseup',   onMouseUp,   true);
+
+                        // UNDO
+
+                        evt.stopPropagation();
+                        evt.preventDefault();
+
+                    },
+                    onMouseMove = function(evt){
+
+                        var newGridSize = Math.floor(gridSize + ((evt.pageX - startX) / 2));
+
+                        if(newGridSize < 7){
+
+                            if(self.get('active')){ 
+                                self.set({ active: false }); 
+                            }
+                            gridControl.style.left = '-20px';
+
+                            newGridSize = 0;
+
+                        }else if(newGridSize > 70){
+
+                            self.set({ gridSize: 70 });
+                            gridControl.style.left = '120px';
+
+                        }else{
+
+                            if(!self.get('active')){ 
+                                self.set({ active: true }); 
+                            }
+                            self.set({ gridSize: newGridSize });
+                            gridControl.style.left = newGridSize * 2 - 20 + 'px';
+
+                        }
+
+                        for(var i = 0, l = allElements.length; i < l; i++){
+
+                            allElements[i].set(allElementDimensions[i]);
+
+                            if(pageWidth && (allElements[i].get('left') + allElements[i].get('width')) > pageWidth){
+
+                                var newLeft  = allElements[i].get('left'),
+                                    newWidth = allElements[i].get('width');
+
+                                while(newLeft + newWidth > pageWidth){
+
+                                    newLeft   = allElements[i].set({ 
+                                                    left:  (allElementDimensions[i].left  - 0.5 * newGridSize)
+                                                });
+
+                                    newWidth  = allElements[i].set({ 
+                                                    width: (allElementDimensions[i].width - newGridSize)
+                                                });
+                                    
+                                }
+
+
+                            }
+                        }
+
+                        
+                    },
+                    onMouseUp = function(evt){
+
+                        document.removeEventListener('mousemove', onMouseMove, true);
+                        document.removeEventListener('mouseup',   onMouseUp,   true);
+                        
+                        SuperGlue.get('document').set({ interactionInProgress: false });
+                        self.set({ visible: wasVisible });
+                        
+                        // UNDO
+                        
+                        evt.stopPropagation();
+                        evt.preventDefault();
+                        
+                    };
+
+                gridControl.style.left = this.get('gridSize') * 2 - 20 + 'px';
+
+                gridControl.addEventListener('mousedown', onMouseDown, false);
+
+
+
+            }
+        }
+
+
+    }
+
+
+}});

+ 70 - 0
src/allplatforms/classes/HTMLEditor.js

@@ -0,0 +1,70 @@
+SC.loadPackage({ 'HTMLEditor': {
+
+    comment: 'This is the HTML editor.',
+
+    traits: ['Window'],
+
+
+    properties: {
+
+        html:       { comment: 'I hold the html which the user wants to edit' },
+        callback:   { comment: 'I hold the callback function, which takes as its single argument the edited HTMLString.' }
+
+    },
+
+    methods: {
+
+    	init: { 
+    		comment: 	'I start a new HTMLEditor. My argument is '+
+                        '{ html: aHTMLString, callback: function(aHTMLString){},'+
+                        '  top: anInt, left: anInt, width: anInt, height: anInt }.',
+    		code: 		function(startConfig){
+
+                var self = this;
+                
+                self.delegate('Window', 'init', startConfig);
+
+                self.set({ 
+                    callback: startConfig.callback,
+                    html:     startConfig.html
+                });
+                
+                var editTextarea = document.createElement('textarea');
+                    editTextarea.classList.add('sg-editing-superuser-textarea');
+                    editTextarea.value = self.get('html');
+
+                    
+
+                self.get('content').appendChild(editTextarea);
+                editTextarea.focus();
+                
+                var modalButtonContainer = document.createElement('div');
+                    modalButtonContainer.classList.add('sg-editing-superuser-modal-container');
+
+                var modalButtonConfirm = document.createElement('button');
+                    modalButtonConfirm.classList.add('confirm');
+                    modalButtonConfirm.addEventListener('click', function() {
+                        self.get('callback').call(self, editTextarea.value);
+                        SuperGlue.get('windowManager').do('closeWindow', self);
+                    });
+
+                var modalButtonCancel = document.createElement('button');
+                    modalButtonCancel.classList.add('cancel');
+                    modalButtonCancel.addEventListener('click', function() {
+                        SuperGlue.get('windowManager').do('closeWindow', self);
+                    });
+
+                modalButtonContainer.appendChild(modalButtonConfirm);
+                modalButtonContainer.appendChild(modalButtonCancel);
+                this.get('content').appendChild(modalButtonContainer);
+
+
+
+    		}
+    	}
+
+
+    }
+
+
+}});

+ 25 - 0
src/allplatforms/classes/History.js

@@ -0,0 +1,25 @@
+SC.loadPackage({ 'History': {
+
+    comment: 'I manage the history of the editing session.',
+
+
+    properties: {
+
+        stack: { comment: 'I hold the action stack.' }
+
+    },
+
+    methods: {
+
+    	init: { 
+    		comment: 	'method comment',
+    		code: 		function(){
+
+    		}
+    	}
+
+
+    }
+
+
+}});

+ 39 - 0
src/allplatforms/classes/IFrameElement.js

@@ -0,0 +1,39 @@
+SC.loadPackage({ 'IFrameElement': {
+
+    comment: 'I define an iframe element.',
+
+    traits: [ 'Element' ],
+    
+
+    sharedProperties: {
+        protoHTML:          { initValue: '<div class="sg-element" data-superglue-type="IFrameElement" style="left: 0px; top: 0px; width: 0px; height: 0px;">'
+                                        +'\t<iframe src="http://localhost/" style="width: 100%; height: 100%;"></iframe>'
+                                        +'</div>' },
+        applicableWidgets:  { initValue: [ 'WidgetBackgroundColor', 'WidgetBorderColor', 'WidgetBorder', 'WidgetBorderRadius', 'WidgetPadding', 'WidgetOpacity', 'WidgetIFrame' ] },
+        creationMenuItem:   { initValue: '<div class="sg-editing-creation-menu-container"><button id="sg-editing-creation-menu-iframeElement" class="sg-editing-creation-menu-button" data-tooltip="Webpage"></button></div>' }
+    },
+
+    properties: {
+
+    },
+
+    methods: {
+
+    	init: { 
+    		comment: 	'I initalize a new IFrameElement for the DOM node given to me as argument.',
+    		code: 		function(aNode){
+
+                this.delegate('Element', 'init', aNode);
+
+                // Custom initialisation here
+
+                
+
+    		}
+    	}
+
+
+    }
+
+
+}});

+ 195 - 0
src/allplatforms/classes/ImageElement.js

@@ -0,0 +1,195 @@
+SC.loadPackage({ 'ImageElement': {
+
+    comment: 'I define an image element.',
+
+    traits: [ 'Element' ],
+
+
+    sharedProperties: {
+        protoHTML:          { initValue: '<div class="sg-element" data-superglue-type="ImageElement" style="left: 0px; top: 0px; width: 0px; height: 0px;">'
+                                        +'\t<div style="width: 100%; height: 100%; background-image: url(\'/resources/default/img/placeholder.png\'); background-repeat: repeat;"></div>'
+                                        +'</div>' },
+        applicableWidgets:  { initValue: [ 'WidgetBackgroundColor', 'WidgetBorderColor', 'WidgetBorder', 'WidgetBorderRadius', 'WidgetPadding', 'WidgetOpacity', 'WidgetImgLink', 'WidgetImgDimensions', 'WidgetImgSrc' ] },
+        creationMenuItem:   { initValue: '<div class="sg-editing-creation-menu-container"><button id="sg-editing-creation-menu-imageElement" class="sg-editing-creation-menu-button" data-tooltip="Image"></button></div>' }
+    },
+
+    properties: {
+
+        imgSource:     {
+            comment: 'I hold the source of the image as string.',
+            transform: function(aString){
+
+                var contentNode   = this.get('node'),
+                    hasLink       = contentNode.firstElementChild.tagName === 'A',
+                    imgDomElement = hasLink
+                                    ? contentNode.firstElementChild.firstElementChild
+                                    : contentNode.firstElementChild,
+                    elementType   = imgDomElement.tagName;
+
+                if(elementType === 'DIV'){
+                    imgDomElement.style.backgroundImage = 'url("' + aString + '")';
+                }else{
+                    imgDomElement.src = aString;
+                }
+
+
+
+                return aString;
+            }
+        },
+
+        dimensions: {
+            comment: 'An ImageElement can have 6 enumerative dimension options: tile, tileX, tileY, stretch, stretchAspectRatio, aspectRatio',
+            transform: function(aSymbolicString){
+
+                var contentNode   = this.get('node'),
+                    hasLink       = contentNode.firstElementChild.tagName === 'A',
+                    imgDomElement = hasLink
+                                    ? contentNode.firstElementChild.firstElementChild
+                                    : contentNode.firstElementChild,
+
+                    imgSrc        = this.get('imgSource'),
+
+                    newImgDomElement;
+
+
+                switch(aSymbolicString){
+                    case 'tile':
+                        newImgDomElement = document.createElement('div');
+                        newImgDomElement.style.width  = '100%';
+                        newImgDomElement.style.height = '100%';
+                        newImgDomElement.style.backgroundImage  = 'url("' + imgSrc + '")';
+                        newImgDomElement.style.backgroundRepeat = 'repeat';
+                        break;
+
+                    case 'tileX':
+                        newImgDomElement = document.createElement('div');
+                        newImgDomElement.style.width  = '100%';
+                        newImgDomElement.style.height = '100%';
+                        newImgDomElement.style.backgroundImage  = 'url("' + imgSrc + '")';
+                        newImgDomElement.style.backgroundRepeat = 'repeat-x';
+                        break;
+
+                    case 'tileY':
+                        newImgDomElement = document.createElement('div');
+                        newImgDomElement.style.width  = '100%';
+                        newImgDomElement.style.height = '100%';
+                        newImgDomElement.style.backgroundImage  = 'url("' + imgSrc + '")';
+                        newImgDomElement.style.backgroundRepeat = 'repeat-y';
+                        break;
+
+                    case 'stretch':
+                        newImgDomElement = document.createElement('img');
+                        newImgDomElement.style.width  = '100%';
+                        newImgDomElement.style.height = '100%';
+                        newImgDomElement.src = imgSrc;
+                        break;
+
+                    case 'stretchAspectRatio':
+                        newImgDomElement = document.createElement('img');
+                        newImgDomElement.style.minWidth  = '100%';
+                        newImgDomElement.style.minHeight = '100%';
+                        newImgDomElement.src = imgSrc;
+                        break;
+
+                    case 'aspectRatio':
+                        newImgDomElement = document.createElement('img');
+                        newImgDomElement.src = imgSrc;
+                        break;
+
+                    default:
+                        newImgDomElement = imgDomElement;
+                        break;
+
+                }
+
+                if(hasLink){
+
+                    contentNode.firstElementChild.removeChild(imgDomElement);
+                    contentNode.firstElementChild.appendChild(newImgDomElement);
+
+                }else{
+
+                    contentNode.removeChild(imgDomElement);
+                    contentNode.appendChild(newImgDomElement);
+
+                }
+
+                return aSymbolicString;
+            }
+        }
+
+    },
+
+    methods: {
+
+    	init: { 
+    		comment: 	'I initalize a new ImageElement for the DOM node given to me as argument.',
+    		code: 		function(aNode){
+
+                this.delegate('Element', 'init', aNode);
+
+                // Custom initialisation here
+                var contentNode   = this.get('node'),
+                    hasLink       = contentNode.firstElementChild.tagName === 'A',
+                    imgDomElement = hasLink
+                                    ? contentNode.firstElementChild.firstElementChild
+                                    : contentNode.firstElementChild,
+
+                    imgSource     = imgDomElement.tagName === 'IMG'
+                                    ? imgDomElement.src
+                                    : imgDomElement.style.backgroundImage.split('url("').join('').split('")').join('')
+                                                                         .split('url(').join('').split(')').join('');
+
+                    if(document.location.origin !== "null"){
+                        imgSource = imgSource.replace(document.location.origin, '')
+                    }
+
+                    this.set({ imgSource: imgSource });
+
+    		}
+    	},
+
+        renderYourself: {
+            comment: 'I represent myself as a string to the compiler.',
+            code: function(config){
+
+                var indent = '\n' + Array(config.indent).join('\t'),
+                    thisElement,
+                    contentNode;
+
+                contentNode = (this.get('node').innerHTML
+                                        .split('\n')
+                                        .filter(function(line){ return line.trim() !== '' })
+                                        .join('\n'));
+
+                if(document.location.origin !== "null"){
+                    contentNode = contentNode.replace(document.location.origin, '')
+                }
+                
+                contentNode = contentNode.replace('url("', 'url(').replace('");', ');');
+
+
+                thisElement = indent + '<div class="sg-element" data-superglue-type="ImageElement" style="'
+                                        +this.get('node').getAttribute('style')
+                                        +'"'
+                                        +(this.get('group') 
+                                            ? (' data-superglue-group="'+ this.get('group') +'"')
+                                            : '')
+                                        +'>'
+
+                            + '\n' +    contentNode
+                            
+                            + indent + '</div>';
+
+
+                return thisElement;
+
+            }
+        }
+
+
+    }
+
+
+}});

+ 21 - 0
src/allplatforms/classes/Keyboard.js

@@ -0,0 +1,21 @@
+SC.loadPackage({ 'Keyboard': {
+
+    comment: 'I am a controller for keyboard commands.',
+
+
+    methods: {
+
+        init: { 
+            comment:    'method comment',
+            code:       function(){
+
+            }
+        }
+
+    }
+
+
+    
+
+
+}});

+ 103 - 0
src/allplatforms/classes/MenuItem.js

@@ -0,0 +1,103 @@
+SC.loadPackage({ 'MenuItem': {
+
+    comment: 'I am the abstract base class of all MenuItems. Subclasses must include me as a trait, and call the init method of this class. Every Subclass must have a shared property called menuContainer, which contains as a string the html of its menuContainer.',
+
+
+    properties: {
+
+        menuContainer:  { comment: 'I store the DOMElement containing my menuContainer.' },
+        menuButton:     { comment: 'I store the DOMElement containing my menuButton.' },
+
+        documentMenu:   { comment: 'I hold a reference to the documentMenu.' },
+
+        isActionButton:     { comment: 'An action button does not stay active after being clicked. I should be set during mySubclass>>init' },
+        isMenuItemActive:   { comment: 'Wether the menuItem is active. Transform function can be overriden by my subclasses to show e.g. a panel.',
+                              transform: function(aBoolean){
+                                    if(aBoolean){
+                                        this.get('menuContainer').classList.add('active');
+                                    }else{
+                                        this.get('menuContainer').classList.remove('active');
+                                    }
+                                    return aBoolean
+                              }
+                            }
+
+    },
+
+    methods: {
+
+    	init: { 
+    		comment: 	'I init a menuItem, therefor all my subclasses must call me via this.delegate(\'MenuItem\', \'init\', theDocumentMenu).',
+    		code: 		function(theDocumentMenu){
+
+                var self = this,
+                    menuContainer = (new DOMParser()).parseFromString(this.class.get('menuContainer'), 'text/html').body.firstChild;
+
+                menuContainer.addEventListener('mouseenter', function(evt){
+
+                    if(theDocumentMenu.get('activeMenuItem') === null){
+                        self.set({ isMenuItemActive: true });
+                    }
+
+                    evt.stopPropagation();
+
+                }, false);
+
+                menuContainer.addEventListener('mouseleave', function(evt){
+
+                    if(theDocumentMenu.get('activeMenuItem') !== self){
+                        self.set({ isMenuItemActive: false });
+                    }
+
+                    evt.stopPropagation();
+
+                }, false);
+                
+
+                menuContainer.addEventListener('mouseup', function(evt){
+
+                    var activeMenuItem = theDocumentMenu.get('activeMenuItem');
+                    if(activeMenuItem === self){
+                        theDocumentMenu.set({ activeMenuItem:   null });
+                    }else if(activeMenuItem === null){
+                        theDocumentMenu.set({ activeMenuItem: self });
+                    }else{
+                        theDocumentMenu.set({ activeMenuItem: self });
+                        activeMenuItem.set({ isMenuItemActive: false });
+                                  self.set({ isMenuItemActive: true  });
+                    }
+
+                    
+                }, true);
+
+                menuContainer.addEventListener('mouseup', function(evt){
+                    if(self.get('isActionButton')){
+                        theDocumentMenu.set({ activeMenuItem:   null  });
+                                   self.set({ isMenuItemActive: false });
+                    }
+                }, false);
+
+
+                menuContainer.addEventListener('mousedown', function(evt){
+                    evt.stopPropagation();
+                }, false);
+
+
+                this.set({
+                    menuContainer:  menuContainer,
+                    menuButton:     menuContainer.firstElementChild,
+                    isActionButton: false,
+                    documentMenu:   theDocumentMenu
+                });
+
+
+
+    		}
+
+    	}
+
+
+    }
+
+
+}});

+ 246 - 0
src/allplatforms/classes/MenuItemBackgroundColor.js

@@ -0,0 +1,246 @@
+SC.loadPackage({ 'MenuItemBackgroundColor': {
+
+    comment: 'I am the MenuItem for the background-color of the page.',
+
+    traits:  ['MenuItem'],
+
+    sharedProperties: {
+
+        menuContainer:  { initValue: '<div class="sg-editing-menu-container"><button id="sg-editing-menu-backgroundColor" class="sg-editing-menu-button"></button></div>' }
+
+    },
+
+    properties: {
+
+        menuPanel:    { comment: 'I store the DOMElement containing the panel of controls of the MenuItem.' },
+
+        isMenuItemActive: { comment: 'Wether the MenuItem is active.',
+                            transform: function(aBoolean){
+                            
+                                if(aBoolean){
+
+                                    if(this.get('menuPanel').parentNode !== this.get('menuContainer')){
+                                        this.get('menuContainer').appendChild(this.get('menuPanel'));
+                                    }
+                                    this.get('menuContainer').classList.add('active');
+
+                                    // update Value
+
+                                }else{
+
+                                    if(this.get('menuPanel').parentNode === this.get('menuContainer')){
+                                        this.get('menuContainer').removeChild(this.get('menuPanel'));
+                                    }
+                                    this.get('menuContainer').classList.remove('active');
+                                
+                                }
+                                return aBoolean
+                            }
+                          }
+
+    },
+
+    methods: {
+
+    	init: { 
+    		comment: 	'I init the MenuItem.',
+    		code: 		function(theDocumentMenu){
+
+                this.delegate('MenuItem', 'init', theDocumentMenu);
+
+                var initialColor = document.body.style.backgroundColor,
+                    pickerLoad   = true;
+
+                this.do('initColorPickerWidget', {
+
+                    theDocumentMenu: theDocumentMenu,
+
+                    initialColor: initialColor,
+
+                    setCallback: function(colorCode){
+
+                        if(pickerLoad){
+                            return pickerLoad = false;
+                        }
+
+                        document.body.style.backgroundColor = colorCode;
+
+                    }
+
+
+                });
+
+
+    		}
+
+    	},
+
+
+
+        initColorPickerWidget: { 
+            comment:    'I init the widget as a ColorPickerWidget.',
+            code:       function(colorPickerConfig){
+
+                var menuPanelCode = '<div class="sg-editing-menu-colorPicker-panel">'
+                                        +'<div class="sg-editing-menu-panel">'
+                                            +'<div class="sg-menu-triangle-right"></div>'
+                                            +'<div class="sg-colorpicker-container"></div>'
+                                        +'</div>'
+                                    +'</div>'
+                    self      = this,
+                    menuPanel = (new DOMParser()).parseFromString(menuPanelCode, 'text/html').body.firstChild;
+                
+                menuPanel.querySelector('.sg-editing-menu-panel').addEventListener('mouseup', function(evt){
+                    colorPickerConfig.theDocumentMenu.set({ activeMenuItem: self });
+                    evt.stopPropagation();
+                }, false);
+
+                this.set({ 
+                    menuPanel: menuPanel
+                });
+
+
+
+                // From flexicolorPicker
+
+
+
+                var colorpickerContainer = menuPanel.querySelector('.sg-colorpicker-container');
+                var colorpicker;
+
+                // Inputs
+
+                var colorpickerInputR,
+                    colorpickerInputB,
+                    colorpickerInputB,
+                    colorpickerInputHex;
+
+
+
+                var start = function() {
+                    
+                    var colorpickerElement = document.createElement('div');
+                    colorpickerElement.classList.add('sg-colorpicker');
+                    colorpickerContainer.appendChild(colorpickerElement);
+
+                    var colorpickerInputContainer = document.createElement('div');
+                        colorpickerInputContainer.classList.add('sg-colorpicker-input-container');
+
+                    colorpickerInputR = document.createElement('input');
+                        colorpickerInputR.setAttribute('type', 'number');
+                        colorpickerInputR.addEventListener('change', function() {
+                            updatePicker(ColorPicker.rgb2hex({ r: this.value, g: colorpickerInputG.value, b: colorpickerInputB.value }));
+                        });
+                    colorpickerInputContainer.appendChild(colorpickerInputR);
+
+                    colorpickerInputG = document.createElement('input');
+                        colorpickerInputG.setAttribute('type', 'number');
+                        colorpickerInputG.addEventListener('change', function() {
+                            updatePicker(ColorPicker.rgb2hex({ r: colorpickerInputR.value, g: this.value, b: colorpickerInputB.value }));
+                        });
+                    colorpickerInputContainer.appendChild(colorpickerInputG);
+
+                    colorpickerInputB = document.createElement('input');
+                        colorpickerInputB.setAttribute('type', 'number');
+                        colorpickerInputB.addEventListener('change', function() {
+                            updatePicker(ColorPicker.rgb2hex({ r: colorpickerInputR.value, g: colorpickerInputG.value, b: this.value }));
+                        });
+                    colorpickerInputContainer.appendChild(colorpickerInputB);
+
+                    colorpickerInputHex = document.createElement('input');
+                        colorpickerInputHex.setAttribute('type', 'text');
+                        colorpickerInputHex.addEventListener('change', function() {
+                            updatePicker(this.value);
+                        });
+                    colorpickerInputContainer.appendChild(colorpickerInputHex);
+
+                    colorpickerContainer.appendChild(colorpickerInputContainer);
+
+
+                    colorpicker = ColorPicker(colorpickerElement, updateColor);
+
+                    updatePicker(initialColor);
+
+                    var topColors = getMostUsedColors();
+                    var topColorsContainer = document.createElement('div');
+                        topColorsContainer.classList.add('sg-colorpicker-top-colors');
+
+                    for (var i=0; i<topColors.length; i++) {
+                        var topColorElement = document.createElement('span');
+                            topColorElement.style.backgroundColor = topColors[i].color;
+                            topColorElement.addEventListener('mousedown', function() {
+                                updatePicker(rgbString2Hex(this.style.backgroundColor));
+                            });
+                        topColorsContainer.appendChild(topColorElement);
+                    }
+
+                    colorpickerContainer.appendChild(topColorsContainer);
+
+                    var transparentElement = document.createElement('div');
+                        transparentElement.classList.add('sg-colorpicker-transparent');
+                        transparentElement.addEventListener('mousedown', function() {
+                            colorPickerConfig.setCallback.call(this, '');
+                        });
+                    colorpickerContainer.appendChild(transparentElement);
+                    
+                }
+
+
+                var updateColor = function(hex) {
+                    
+                    var rgb = ColorPicker.hex2rgb(hex);
+
+                    colorpickerInputHex.value = hex;
+                    
+                    colorpickerInputR.value = rgb.r;
+                    colorpickerInputG.value = rgb.g;
+                    colorpickerInputB.value = rgb.b;
+                    
+                    colorPickerConfig.setCallback.call(this, 'rgb('+rgb.r+', '+rgb.g+', '+rgb.b+')');
+                }
+
+                var updatePicker = function(hex) {
+                    
+                    colorpicker.setHex(hex);
+                }
+
+                var rgbString2Hex = function(rgbString) {
+                     if(rgbString === ''){
+                        return '';
+                     }
+                     if (  rgbString.search("rgb") == -1 ) {
+                          return rgbString;
+                     } else {
+                          rgbString = rgbString.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+))?\)$/);
+                          function hex(x) {
+                               return ("0" + parseInt(x).toString(16)).slice(-2);
+                          }
+                          return "#" + hex(rgbString[1]) + hex(rgbString[2]) + hex(rgbString[3]); 
+                     }
+                };
+
+                var getMostUsedColors = function() {
+                    return [];
+
+                }
+
+                var initialColor = rgbString2Hex(colorPickerConfig.initialColor);
+                start();
+
+
+
+
+
+
+
+                // End from flexicolorPicker
+
+            }
+
+        }
+
+
+    }
+
+
+}});

+ 128 - 0
src/allplatforms/classes/MenuItemBackgroundImg.js

@@ -0,0 +1,128 @@
+SC.loadPackage({ 'MenuItemBackgroundImg': {
+
+    comment: 'I am the MenuItem for the background-image of the page.',
+
+    traits:  ['MenuItem'],
+
+    sharedProperties: {
+
+        menuContainer:  { initValue: '<div class="sg-editing-menu-container"><button id="sg-editing-menu-backgroundImg" class="sg-editing-menu-button"></button></div>' },
+
+        menuPanel:    { initValue:   '<div id="sg-editing-menu-backgroundImg-panel">'
+                                            +'<div class="sg-editing-menu-panel">'
+                                                +'<div class="sg-menu-triangle-right"></div>'
+                                                +'<input  id="sg-editing-menu-backgroundImg-input" type="text"></input>'
+                                                +'<button id="sg-editing-menu-backgroundImg-chooseFile" class="sg-editing-menu-button"></button>'
+                                                +'<button id="sg-editing-menu-backgroundImg-clear" class="sg-editing-menu-button"></button>'
+                                            +'</div>'
+                                        +'</div>' }
+
+    },
+
+    properties: {
+
+        menuPanel:    { comment: 'I store the DOMElement containing the panel of controls of the menu.' },
+
+        isMenuItemActive: { comment: 'Wether the MenuItem is active.',
+                          transform: function(aBoolean){
+
+                                if(aBoolean){
+
+                                    if(this.get('menuPanel').parentNode !== this.get('menuContainer')){
+                                        this.get('menuContainer').appendChild(this.get('menuPanel'));
+                                    }
+                                    this.get('menuContainer').classList.add('active');
+
+                                    var srcURL = document.body.style.backgroundImage
+                                                    .split('url("').join('').split('")').join('')
+                                                    .split('url(').join('').split(')').join('')
+                                                    .replace(document.location.origin, '');
+                                    
+                                    this.get('menuPanel').querySelector('#sg-editing-menu-backgroundImg-input').value = srcURL;
+                                    
+
+                                }else{
+
+                                    if(this.get('menuPanel').parentNode === this.get('menuContainer')){
+                                        this.get('menuContainer').removeChild(this.get('menuPanel'));
+                                    }
+                                    this.get('menuContainer').classList.remove('active');
+                                
+                                }
+                                return aBoolean
+                          }
+                        }
+
+    },
+
+
+    methods: {
+
+    	init: { 
+    		comment: 	'I init the MenuItem.',
+    		code: 		function(theDocumentMenu){
+
+                this.delegate('MenuItem', 'init', theDocumentMenu);
+
+                var self = this,
+                    menuPanel = (new DOMParser()).parseFromString(this.class.get('menuPanel'), 'text/html').body.firstChild;
+                
+
+
+                menuPanel.querySelector('#sg-editing-menu-backgroundImg-input').addEventListener('mouseup', function(evt){
+                    theDocumentMenu.set({ activeMenuItem: self });
+                    evt.stopPropagation();
+                }, false);
+
+                menuPanel.querySelector('#sg-editing-menu-backgroundImg-input').addEventListener('input', function(evt){
+                    self.do('setBackgroundImg', this.value)
+                }, false);
+
+                menuPanel.querySelector('#sg-editing-menu-backgroundImg-chooseFile').addEventListener('mouseup', function(evt){
+                    theDocumentMenu.set({ activeMenuItem: self });
+                    evt.stopPropagation();
+
+                    var pathParser = document.createElement('a');
+                    pathParser.href = menuPanel.querySelector('#sg-editing-menu-backgroundImg-input').value;
+
+                    SuperGlue.get('fileManager').do('chooseFile', {
+                        oldPath: pathParser.pathName,
+                        callback: function(srcPath){
+                                        menuPanel.querySelector('#sg-editing-menu-backgroundImg-input').value = srcPath;
+                                        self.do('setBackgroundImg', srcPath);
+                                    }
+                    });
+                }, false);
+
+                menuPanel.querySelector('#sg-editing-menu-backgroundImg-clear').addEventListener('mouseup', function(evt){
+                    theDocumentMenu.set({ activeMenuItem: self });
+                    evt.stopPropagation();
+
+                    menuPanel.querySelector('#sg-editing-menu-backgroundImg-input').value = '';
+                    self.do('setBackgroundImg', '');
+                }, false);
+
+
+                this.set({ 
+                    menuPanel: menuPanel
+                });
+
+
+    		}
+
+    	},
+
+        setBackgroundImg: {
+            comment: 'I set the source of the body background-image',
+            code: function(srcURL){
+
+                document.body.style.backgroundImage = srcURL === '' ? '' : 'url("' + srcURL + '")';
+
+            }
+        }
+
+
+    }
+
+
+}});

+ 107 - 0
src/allplatforms/classes/MenuItemBackgroundRepeat.js

@@ -0,0 +1,107 @@
+SC.loadPackage({ 'MenuItemBackgroundRepeat': {
+
+    comment: 'I am the MenuItem for the background-repeat of the page.',
+
+    traits:  ['MenuItem'],
+
+    sharedProperties: {
+
+        menuContainer:  { initValue: '<div class="sg-editing-menu-container"><button id="sg-editing-menu-backgroundRepeat" class="sg-editing-menu-button"></button></div>' },
+
+        menuPanel: { initValue: '<div id="sg-editing-menu-backgroundRepeat-panel">'
+                                        +'<div class="sg-editing-menu-panel">'
+                                            +'<div class="sg-menu-triangle-right"></div>'
+                                            +'<button id="sg-editing-menu-backgroundRepeat-tile"        data-superglue-backgroundRepeat="tile" class="sg-editing-menu-button"></button>'
+                                            +'<button id="sg-editing-menu-backgroundRepeat-tileX"       data-superglue-backgroundRepeat="tileX" class="sg-editing-menu-button"></button>'
+                                            +'<button id="sg-editing-menu-backgroundRepeat-tileY"       data-superglue-backgroundRepeat="tileY" class="sg-editing-menu-button"></button>'
+                                        +'</div>'
+                                    +'</div>' 
+                     }
+
+
+    },
+
+    properties: {
+
+        menuPanel:    { comment: 'I store the DOMElement containing the panel of controls of the menu.' },
+
+        isMenuItemActive: { comment: 'Wether the menu is active.',
+                          transform: function(aBoolean){
+
+                                if(aBoolean){
+
+                                    if(this.get('menuPanel').parentNode !== this.get('menuContainer')){
+                                        this.get('menuContainer').appendChild(this.get('menuPanel'));
+                                    }
+                                    this.get('menuContainer').classList.add('active');
+
+
+                                }else{
+
+                                    if(this.get('menuPanel').parentNode === this.get('menuContainer')){
+                                        this.get('menuContainer').removeChild(this.get('menuPanel'));
+                                    }
+                                    this.get('menuContainer').classList.remove('active');
+                                
+                                }
+                                return aBoolean
+                          }
+                        }
+
+    },
+
+    methods: {
+
+    	init: { 
+    		comment: 	'I init the MenuItem.',
+    		code: 		function(theDocumentMenu){
+
+                this.delegate('MenuItem', 'init', theDocumentMenu);
+
+                var self = this,
+                    menuPanel = (new DOMParser()).parseFromString(this.class.get('menuPanel'), 'text/html').body.firstChild,
+
+                    onMouseUp = function(evt){
+
+                        var newDim = this.getAttribute('data-superglue-backgroundRepeat');
+                        
+                        switch(newDim){
+                            case 'tileX':
+                                document.body.style.backgroundRepeat = 'repeat-x'
+                                break;
+
+                            case 'tileY':
+                                document.body.style.backgroundRepeat = 'repeat-y'
+                                break;
+
+                            default:
+                                document.body.style.backgroundRepeat = 'repeat'
+                                break;
+                        }
+
+                        evt.stopPropagation()
+
+                    },
+
+                    buttons = menuPanel.querySelectorAll('.sg-editing-menu-button');
+
+
+                for(var i = buttons.length - 1; i >= 0; i--){
+                    buttons[i].addEventListener('mouseup', onMouseUp, false)
+                }
+                
+
+                this.set({ 
+                    menuPanel: menuPanel
+                });
+
+
+    		}
+
+    	}
+
+
+    }
+
+
+}});

+ 45 - 0
src/allplatforms/classes/MenuItemCenter.js

@@ -0,0 +1,45 @@
+SC.loadPackage({ 'MenuItemCenter': {
+
+    comment: 'I am the MenuItem for switching the layout either in centered mode or in infinite space.',
+
+    traits:  ['MenuItem'],
+
+    sharedProperties: {
+
+        menuContainer:  { initValue: '<div class="sg-editing-menu-container"><button id="sg-editing-menu-center" class="sg-editing-menu-button"></button></div>' }
+
+    },
+
+    methods: {
+
+    	init: { 
+    		comment: 	'I init the MenuItem.',
+    		code: 		function(theDocumentMenu){
+
+                this.delegate('MenuItem', 'init', theDocumentMenu);
+                this.set({ isActionButton: true });
+                
+                this.get('menuContainer').firstChild.addEventListener('mouseup', function(evt){
+
+                    var myDocument = SuperGlue.get('document');
+
+                    myDocument.set({ layout: {
+                        
+                        centered: !myDocument.get('layout').centered
+
+                    }});
+
+                    
+
+                }, false);
+
+
+    		}
+
+    	}
+
+
+    }
+
+
+}});

+ 39 - 0
src/allplatforms/classes/MenuItemFileManager.js

@@ -0,0 +1,39 @@
+SC.loadPackage({ 'MenuItemFileManager': {
+
+    comment: 'I am the MenuItem invoking the FileManger.',
+
+    traits:  ['MenuItem'],
+
+    sharedProperties: {
+
+        menuContainer:  { initValue: '<div class="sg-editing-menu-container"><button id="sg-editing-menu-fileManager" class="sg-editing-menu-button"></button></div>' }
+
+    },
+
+    methods: {
+
+    	init: { 
+    		comment: 	'I init the MenuItem.',
+    		code: 		function(theDocumentMenu){
+
+                this.delegate('MenuItem', 'init', theDocumentMenu);
+                this.set({ isActionButton: true });
+                
+                this.get('menuContainer').firstChild.addEventListener('mouseup', function(evt){
+
+                    SuperGlue.get('fileManager').do('open')
+
+                    theDocumentMenu.do('close');
+
+                }, false);
+
+
+    		}
+
+    	}
+
+
+    }
+
+
+}});

+ 43 - 0
src/allplatforms/classes/MenuItemOutlines.js

@@ -0,0 +1,43 @@
+SC.loadPackage({ 'MenuItemOutlines': {
+
+    comment: 'I am the MenuItem for switching the outlines on and off.',
+
+    traits:  ['MenuItem'],
+
+    sharedProperties: {
+
+        menuContainer:  { initValue: '<div class="sg-editing-menu-container"><button id="sg-editing-menu-outlines" class="sg-editing-menu-button"></button></div>' }
+
+    },
+
+    methods: {
+
+    	init: { 
+    		comment: 	'I init the MenuItem.',
+    		code: 		function(theDocumentMenu){
+
+                this.delegate('MenuItem', 'init', theDocumentMenu);
+                this.set({ isActionButton: true });
+                
+                this.get('menuContainer').firstChild.addEventListener('mouseup', function(evt){
+
+                    var myDocument = SuperGlue.get('document');
+                    myDocument.set({ 
+                        
+                        showOutlines: (! myDocument.get('showOutlines'))
+
+                    });
+
+
+                }, false);
+
+
+    		}
+
+    	}
+
+
+    }
+
+
+}});

+ 107 - 0
src/allplatforms/classes/MenuItemPageTitle.js

@@ -0,0 +1,107 @@
+SC.loadPackage({ 'MenuItemPageTitle': {
+
+    comment: 'I am the MenuItem for the page title.',
+
+    traits:  ['MenuItem'],
+
+    sharedProperties: {
+
+        menuContainer:  { initValue: '<div class="sg-editing-menu-container"><button id="sg-editing-menu-pageTitle" class="sg-editing-menu-button"></button></div>' },
+
+        menuPanel:    { initValue:   '<div id="sg-editing-menu-pageTitle-panel">'
+                                            +'<div class="sg-editing-menu-panel">'
+                                                +'<div class="sg-menu-triangle-right"></div>'
+                                                +'<input  id="sg-editing-menu-pageTitle-input" type="text"></input>'
+                                                +'<button id="sg-editing-menu-pageTitle-clear" class="sg-editing-menu-button"></button>'
+                                            +'</div>'
+                                        +'</div>' }
+
+    },
+
+    properties: {
+
+        menuPanel:    { comment: 'I store the DOMElement containing the panel of controls of the menu.' },
+
+        isMenuItemActive: { comment: 'Wether the MenuItem is active.',
+                          transform: function(aBoolean){
+
+                                if(aBoolean){
+
+                                    if(this.get('menuPanel').parentNode !== this.get('menuContainer')){
+                                        this.get('menuContainer').appendChild(this.get('menuPanel'));
+                                    }
+                                    this.get('menuContainer').classList.add('active');
+
+                                    this.get('menuPanel').querySelector('#sg-editing-menu-pageTitle-input').value = (
+                                        document.getElementsByTagName('title')[0].innerHTML
+                                    );
+                                    
+
+                                }else{
+
+                                    if(this.get('menuPanel').parentNode === this.get('menuContainer')){
+                                        this.get('menuContainer').removeChild(this.get('menuPanel'));
+                                    }
+                                    this.get('menuContainer').classList.remove('active');
+                                
+                                }
+                                return aBoolean
+                          }
+                        }
+
+    },
+
+    methods: {
+
+    	init: { 
+    		comment: 	'I init the MenuItem.',
+    		code: 		function(theDocumentMenu){
+
+                this.delegate('MenuItem', 'init', theDocumentMenu);
+
+                var self = this,
+                    menuPanel = (new DOMParser()).parseFromString(this.class.get('menuPanel'), 'text/html').body.firstChild;
+                
+
+
+                menuPanel.querySelector('#sg-editing-menu-pageTitle-input').addEventListener('mouseup', function(evt){
+                    theDocumentMenu.set({ activeMenuItem: self });
+                    evt.stopPropagation();
+                }, false);
+
+                menuPanel.querySelector('#sg-editing-menu-pageTitle-input').addEventListener('input', function(evt){
+                    self.do('setPageTitle', this.value)
+                }, false);
+
+                menuPanel.querySelector('#sg-editing-menu-pageTitle-clear').addEventListener('mouseup', function(evt){
+                    theDocumentMenu.set({ activeMenuItem: self });
+                    evt.stopPropagation();
+
+                    menuPanel.querySelector('#sg-editing-menu-pageTitle-input').value = '';
+                    self.do('setPageTitle', '');
+                }, false);
+
+
+                this.set({ 
+                    menuPanel: menuPanel
+                });
+
+
+    		}
+
+    	},
+
+        setPageTitle: {
+            comment: 'I set the page title.',
+            code: function(titleString){
+
+                document.getElementsByTagName('title')[0].innerHTML = titleString;
+
+            }
+        }
+
+
+    }
+
+
+}});

+ 32 - 0
src/allplatforms/classes/MenuItemPaste.js

@@ -0,0 +1,32 @@
+SC.loadPackage({ 'MenuItemPaste': {
+
+    comment: 'I am the MenuItem for pasting from the clipboard.',
+
+    traits:  ['MenuItem'],
+
+    sharedProperties: {
+
+        menuContainer:  { initValue: '<div class="sg-editing-menu-container"><button id="sg-editing-menu-paste" class="sg-editing-menu-button"></button></div>' }
+
+    },
+
+    methods: {
+
+    	init: { 
+    		comment: 	'I init the MenuItem.',
+    		code: 		function(theDocumentMenu){
+
+                this.delegate('MenuItem', 'init', theDocumentMenu);
+                this.set({ isActionButton: true });
+
+
+
+    		}
+
+    	}
+
+
+    }
+
+
+}});

+ 33 - 0
src/allplatforms/classes/MenuItemRedo.js

@@ -0,0 +1,33 @@
+SC.loadPackage({ 'MenuItemRedo': {
+
+    comment: 'I am the MenuItem for redo.',
+
+    traits:  ['MenuItem'],
+
+    sharedProperties: {
+
+        menuContainer:  { initValue: '<div class="sg-editing-menu-container"><button id="sg-editing-menu-redo" class="sg-editing-menu-button"></button></div>' }
+
+    },
+
+    methods: {
+
+    	init: { 
+    		comment: 	'I init the MenuItem.',
+    		code: 		function(theDocumentMenu){
+
+                this.delegate('MenuItem', 'init', theDocumentMenu);
+                this.set({ isActionButton: true });
+
+
+
+
+    		}
+
+    	}
+
+
+    }
+
+
+}});

+ 41 - 0
src/allplatforms/classes/MenuItemSave.js

@@ -0,0 +1,41 @@
+SC.loadPackage({ 'MenuItemSave': {
+
+    comment: 'I am the MenuItem for saving the page.',
+
+    traits:  ['MenuItem'],
+
+    sharedProperties: {
+
+        menuContainer:  { initValue: '<div class="sg-editing-menu-container"><button id="sg-editing-menu-save" class="sg-editing-menu-button"></button></div>' }
+
+    },
+
+    methods: {
+
+    	init: { 
+    		comment: 	'I init the MenuItem.',
+    		code: 		function(theDocumentMenu){
+
+                this.delegate('MenuItem', 'init', theDocumentMenu);
+                this.set({ isActionButton: true });
+
+                this.get('menuContainer').firstChild.addEventListener('mouseup', function(evt){
+
+                    SuperGlue.do('savePage', {
+                        path: document.location.pathname
+                    });
+
+                    theDocumentMenu.do('close');
+
+                }, false);
+
+
+    		}
+
+    	}
+
+
+    }
+
+
+}});

+ 45 - 0
src/allplatforms/classes/MenuItemSaveAs.js

@@ -0,0 +1,45 @@
+SC.loadPackage({ 'MenuItemSaveAs': {
+
+    comment: 'I am the MenuItem for saving the page under a different filename or even to a different domain.',
+
+    traits:  ['MenuItem'],
+
+    sharedProperties: {
+
+        menuContainer:  { initValue: '<div class="sg-editing-menu-container"><button id="sg-editing-menu-saveAs" class="sg-editing-menu-button"></button></div>' }
+
+    },
+
+    methods: {
+
+    	init: { 
+    		comment: 	'I init the MenuItem.',
+    		code: 		function(theDocumentMenu){
+
+                this.delegate('MenuItem', 'init', theDocumentMenu);
+
+                this.get('menuContainer').firstChild.addEventListener('mouseup', function(evt){
+
+
+                    SuperGlue.get('fileManager').do('saveAs', function(filePath){
+
+                        SuperGlue.do('savePage', {
+                            path: filePath
+                        })
+
+                    });
+
+                    
+
+                }, false);
+
+
+    		}
+
+    	}
+
+
+    }
+
+
+}});

+ 105 - 0
src/allplatforms/classes/MenuItemSaveRemote.js

@@ -0,0 +1,105 @@
+SC.loadPackage({ 'MenuItemSaveRemote': {
+
+    comment: 'I am the MenuItem for saving the page to a remote URL.',
+
+    traits:  ['MenuItem'],
+
+    sharedProperties: {
+
+        menuContainer:  { initValue: '<div class="sg-editing-menu-container"><button id="sg-editing-menu-saveRemote" class="sg-editing-menu-button"></button></div>' },
+
+        menuPanel:    { initValue:   '<div id="sg-editing-menu-saveRemote-panel">'
+                                            +'<div class="sg-editing-menu-panel">'
+                                                +'<div class="sg-menu-triangle-top"></div>'
+                                                +'<input  id="sg-editing-menu-saveRemote-input" type="text" placeholder="http://mydomain/mypage.html"></input>'
+                                                +'<button id="sg-editing-menu-saveRemote-confirm" class="sg-editing-menu-button"></button>'
+                                            +'</div>'
+                                        +'</div>' }
+
+    },
+
+    properties: {
+
+        menuPanel:    { comment: 'I store the DOMElement containing the panel of controls of the menu.' },
+
+        isMenuItemActive: { comment: 'Wether the MenuItem is active.',
+                          transform: function(aBoolean){
+
+                                if(aBoolean){
+
+                                    if(this.get('menuPanel').parentNode !== this.get('menuContainer')){
+                                        this.get('menuContainer').appendChild(this.get('menuPanel'));
+                                    }
+                                    this.get('menuContainer').classList.add('active');
+
+                                }else{
+
+                                    if(this.get('menuPanel').parentNode === this.get('menuContainer')){
+                                        this.get('menuContainer').removeChild(this.get('menuPanel'));
+                                    }
+                                    this.get('menuContainer').classList.remove('active');
+                                
+                                }
+                                return aBoolean
+                          }
+                        }
+
+    },
+
+    methods: {
+
+    	init: { 
+    		comment: 	'I init the MenuItem.',
+    		code: 		function(theDocumentMenu){
+
+                this.delegate('MenuItem', 'init', theDocumentMenu);
+
+                var self = this,
+                    menuPanel = (new DOMParser()).parseFromString(this.class.get('menuPanel'), 'text/html').body.firstChild;
+                
+
+
+                menuPanel.querySelector('#sg-editing-menu-saveRemote-input').addEventListener('mouseup', function(evt){
+                    theDocumentMenu.set({ activeMenuItem: self });
+                    evt.stopPropagation();
+                }, false);
+
+                menuPanel.querySelector('#sg-editing-menu-saveRemote-confirm').addEventListener('mouseup', function(evt){
+                    theDocumentMenu.set({ activeMenuItem: self });
+                    evt.stopPropagation();
+
+                    self.do('saveRemote', menuPanel.querySelector('#sg-editing-menu-saveRemote-input').value);
+                }, false);
+
+
+                this.set({ 
+                    menuPanel: menuPanel
+                });
+
+
+    		}
+
+    	},
+
+        saveRemote: {
+            comment: 'I set the page title.',
+            code: function(titleString){
+
+                var url    = this.get('menuPanel').querySelector('#sg-editing-menu-saveRemote-input').value,
+                    parser = document.createElement('a');
+                    
+                parser.href = url;
+                
+                SuperGlue.do('savePage', {
+                    remote: parser.protocol + '//' + parser.host,
+                    path:   parser.pathname
+                })
+
+            }
+        }
+
+
+    }
+
+
+}});

+ 32 - 0
src/allplatforms/classes/MenuItemUndo.js

@@ -0,0 +1,32 @@
+SC.loadPackage({ 'MenuItemUndo': {
+
+    comment: 'I am the MenuItem for Undo.',
+
+    traits:  ['MenuItem'],
+
+    sharedProperties: {
+
+        menuContainer:  { initValue: '<div class="sg-editing-menu-container"><button id="sg-editing-menu-undo" class="sg-editing-menu-button"></button></div>' }
+
+    },
+
+    methods: {
+
+    	init: { 
+    		comment: 	'I init the MenuItem.',
+    		code: 		function(theDocumentMenu){
+
+                this.delegate('MenuItem', 'init', theDocumentMenu);
+                this.set({ isActionButton: true });
+
+
+
+    		}
+
+    	}
+
+
+    }
+
+
+}});

+ 389 - 0
src/allplatforms/classes/ResizeHandles.js

@@ -0,0 +1,389 @@
+SC.loadPackage({ 'ResizeHandles': {
+
+    comment: 'I am a set of resize handles for one element. I also control their behavior and their effect on the element.',
+
+    sharedProperties: {
+        handles: { initValue: '<div class="sg-editing-resize-handles"><div class="sg-editing-resize-handle nwgrip"></div><div class="sg-editing-resize-handle negrip"></div><div class="sg-editing-resize-handle swgrip"></div><div class="sg-editing-resize-handle segrip"></div><div class="sg-editing-resize-handle ngrip"></div><div class="sg-editing-resize-handle egrip"></div><div class="sg-editing-resize-handle sgrip"></div><div class="sg-editing-resize-handle wgrip"></div></div>' },
+    },
+
+    properties: {
+
+        myElement:  { comment: 'The element I belong to.' },
+        node:       { comment: 'My DOM node.' },
+        top:        { transform: function(cssString){ return this.get('node').style.top = cssString; } },
+        left:       { transform: function(cssString){ return this.get('node').style.left = cssString; } },
+        width:      { transform: function(cssString){ return this.get('node').style.width = cssString; } },
+        height:     { transform: function(cssString){ return this.get('node').style.height = cssString; } },
+
+        mouseOnElement: { comment: 'Is the pointer over myElement\'s node?' },
+        mouseOnHandle:  { comment: 'Is the pointer over one of my handle nodes?' },
+        
+        selected:   { comment: 'When my Element is selected, these ResizeHandles should be permantly visible.',
+                      transform: function(aBoolean){
+                            if(aBoolean){
+                                this.do('showResizeHandles');
+                            }else{
+                                this.do('hideResizeHandles', true);
+                            }
+                            return aBoolean;
+                        } 
+                    }
+    },
+
+    methods: {
+
+        init: { 
+            comment:    'I initialize a set of ResizeHandles for a given Element.',
+            code:       function(anElement){
+
+                var self = this,
+                    elementNode = anElement.get('node'),
+                    handlesNode = (new DOMParser()).parseFromString(this.class.get('handles'), 'text/html').body.firstChild,
+                    allResizeHandles = handlesNode.querySelectorAll('.sg-editing-resize-handle');;
+
+                this.set({ mouseOnElement: false, mouseOnHandle: false });
+                
+
+                // Set-up the event listeners for show/hide on myElement's node
+                elementNode.addEventListener('mouseover', function(evt){ 
+                    self.set({ mouseOnElement: true });
+                    self.do('showResizeHandles');
+                }, false);
+                elementNode.addEventListener('mouseout', function(evt){
+                    self.set({ mouseOnElement: false });
+                    self.do('hideResizeHandles');
+                }, false);
+
+                // Set-up the event listeners for show/hide on all my handles' nodes
+                for(var i = 0, l = allResizeHandles.length; i < l; i++){
+                    allResizeHandles.item(i).addEventListener('mouseover', function(evt){ 
+                        self.set({ mouseOnHandle:true });
+                        evt.stopPropagation();
+                    }, true);
+                    allResizeHandles.item(i).addEventListener('mouseout', function(evt){ 
+                        self.set({ mouseOnHandle:false });
+                        self.do('hideResizeHandles');
+                        evt.stopPropagation();
+                    }, true);
+                }
+
+
+                // Set-up the resizing functionality
+                this.do('initResize', {
+                    myElement:        anElement,
+                    allResizeHandles: allResizeHandles
+                });
+
+
+                // Store references
+                this.set({ 
+                    myElement:  anElement,
+                    node:       handlesNode
+                });
+
+            }
+        },
+
+        showResizeHandles: { 
+            comment:    'I show the ResizeHandles.',
+            code:       function(){
+                var handlesNode      = this.get('node'),
+                    editingContainer = SuperGlue.get('document').get('editingContainer');
+                this.set({ mouseOnElement: true });
+                if(handlesNode.parentNode !== editingContainer){
+                    editingContainer.appendChild(handlesNode);
+                }
+
+            }
+        },
+
+        hideResizeHandles: { 
+            comment:    'I hide the ResizeHandles.',
+            code:       function(forceHide){
+
+                if( !this.get('selected') || forceHide){
+                    var self = this,
+                        handlesNode      = this.get('node'),
+                        editingContainer = SuperGlue.get('document').get('editingContainer');
+                
+                    window.setTimeout(function(){
+                        if(     handlesNode.parentNode === editingContainer 
+                            && !self.get('mouseOnElement') 
+                            && !self.get('mouseOnHandle') ){
+                            editingContainer.removeChild(handlesNode);
+                        }
+                    }, 20);
+
+                }
+
+
+            }
+        },
+
+        initResize: { 
+            comment:    'I init the resize functionality for all my handles.',
+            code:       function(config){
+
+                // REMARK: THE CODE IN THIS FUNCTION IS HIGHLY OPTIMISED FOR SPEED,
+                //         NOT READABILITY, BE CAREFUL WHEN YOU TOUCH IT!
+                
+                var self          = this,
+                    myElement     = config.myElement,
+                    selection     = SuperGlue.get('selection'),
+                    wasSelected   = false,
+                    startX        = 0,
+                    startY        = 0,
+                    maxTop        = 0,
+                    maxLeft       = 0,
+                    pageWidth,
+                    infiniteSpace,
+                    widthMarkersVisible,
+                    gridVisible,
+
+                    virtual       = {
+                                        'top'    : 0,
+                                        'left'   : 0,
+                                        'width'  : 0,
+                                        'height' : 0
+                                    },
+                    twoAxes       = null,
+                    changeOne     = null,
+                    changeTwo     = null,
+                    handlerMap    = {
+                                    //  css class   | 2-axes |  y-axis     |   x-axis   |
+                                        'nwgrip'  : [ true   ,  'top'      ,   'left'   ],
+                                        'negrip'  : [ true   ,  'top'      ,   'width'  ],
+                                        'swgrip'  : [ true   ,  'height'   ,   'left'   ],
+                                        'segrip'  : [ true   ,  'height'   ,   'width'  ],
+                                    //                       | x-or-y-axis | is-x-axis? |
+                                        'ngrip'   : [ false  ,  'top'      ,    false   ],
+                                        'sgrip'   : [ false  ,  'height'   ,    false   ],
+                                        'wgrip'   : [ false  ,  'left'     ,    true    ],
+                                        'egrip'   : [ false  ,  'width'    ,    true    ]
+                                    },
+                    
+                    onMouseDown = function(evt){
+
+                        if(evt.button !== 0) return;
+
+                        var widthMarkers = SuperGlue.get('document').get('widthMarkers');
+                            grid         = SuperGlue.get('document').get('grid');
+                        widthMarkersVisible = widthMarkers.get('visible');
+                        gridVisible         = grid.get('visible');
+                        widthMarkers.set({ visible: true });
+                        grid.set({ visible: true });
+
+                        startX          = evt.pageX;
+                        startY          = evt.pageY;
+                        
+                        virtual.top    = myElement.get('top');
+                        virtual.left   = myElement.get('left');
+                        virtual.width  = myElement.get('width');
+                        virtual.height = myElement.get('height');
+                        maxTop  = virtual.top + virtual.height - 50;
+                        maxLeft = virtual.left + virtual.width - 50;
+
+                        infiniteSpace = ! SuperGlue.get('document').get('layout').centered;
+                        pageWidth     =   SuperGlue.get('document').get('layout').width;
+                        if(grid.get('active')){
+                            var gridSize = grid.get('gridSize');
+                            pageWidth = Math.floor(pageWidth / gridSize) * gridSize;
+                        }
+
+
+                        wasSelected = self.get('selected');
+
+                        var classList = evt.target.classList;
+                        for(var i = classList.length - 1; i > -1; i--){
+                            var handlerMapItem = handlerMap[classList.item(i)];
+                            if(handlerMapItem) {
+                                twoAxes   = handlerMapItem[0];
+                                changeOne = handlerMapItem[1];
+                                changeTwo = handlerMapItem[2]
+                                break;
+                            }
+                        }
+                        
+                        
+                        SuperGlue.get('document').set({ interactionInProgress: true });
+                        self.set({ selected: true });
+                        document.addEventListener('mousemove', onMouseMove, true);
+                        document.addEventListener('mouseup',   onMouseUp,   true);
+
+                        // UNDO
+
+                        evt.stopPropagation();
+                        evt.preventDefault();
+                    },
+
+                    onMouseUp = function(evt){
+                        
+                        document.removeEventListener('mousemove', onMouseMove, true);
+                        document.removeEventListener('mouseup',   onMouseUp,   true);
+                        
+                        var myDocument = SuperGlue.get('document');
+                        myDocument.do('afterLayoutHasChanged');
+                        myDocument.set({ interactionInProgress: false });
+
+                        SuperGlue.get('document').get('widthMarkers').set({ visible: widthMarkersVisible });
+                        SuperGlue.get('document').get('grid').set({ visible: gridVisible });
+                        
+                        self.set({ 
+                            mouseOnElement: false,
+                            mouseOnHandle: false
+                        });
+                        self.set({ selected: wasSelected });
+
+                        // UNDO
+                        
+
+                        evt.stopPropagation();
+                        evt.preventDefault();
+                    },
+
+                    onMouseMove = function(evt){
+
+                        var diffX = evt.pageX - startX,
+                            diffY = evt.pageY - startY;
+
+                        // distinction for handlers with two free axes or one
+                        if(twoAxes){
+
+                            // changeOne is here dimension in y-axis,
+                            // changeTwo is here dimension in x-axis
+
+                            virtual[changeOne] += diffY;
+                            virtual[changeTwo] += diffX;
+
+
+                            
+                            if(changeOne === 'top'){
+                                virtual.height -= diffY;
+                            }
+                            if(changeTwo === 'left'){
+                                virtual.width -= diffX;
+                            }
+
+
+                            if(virtual.left >= 0){
+                                if(infiniteSpace || (virtual.width + virtual.left) <= pageWidth){
+                                    myElement.set({ 
+                                        left:  virtual.left < maxLeft ? virtual.left : maxLeft,
+                                        width: virtual.width
+                                    });
+                                }else{
+                                    myElement.set({ 
+                                        left:  virtual.left < maxLeft ? virtual.left : maxLeft,
+                                        width: pageWidth - virtual.left
+                                    });
+                                }
+                            }else{
+                                myElement.set({ 
+                                    left:  0,
+                                    width: virtual.width + virtual.left
+                                });
+                            }
+
+
+                            if(virtual.top >= 0){
+                                myElement.set({ 
+                                    top:    virtual.top < maxTop ? virtual.top : maxTop,
+                                    height: virtual.height
+                                });
+                            }else{
+                                myElement.set({ 
+                                    top:  0,
+                                    height: virtual.height + virtual.top
+                                });
+                            }
+                            
+                            
+
+                        }else{
+
+                            // changeOne is here dimension in y- or x-axis,
+                            // changeTwo is true for x-axis, and for y-axis is false
+                            if(changeTwo){
+
+
+                                virtual[changeOne] += diffX;
+                                if(changeOne === 'left'){
+                                    virtual.width -= diffX;
+                                }
+                                if(virtual.left >= 0){
+                                    if(infiniteSpace || (virtual.width + virtual.left) <= pageWidth){
+                                        myElement.set({ 
+                                            left:  virtual.left < maxLeft ? virtual.left : maxLeft,
+                                            width: virtual.width
+                                        });
+                                    }else{
+                                        myElement.set({ 
+                                            left:  virtual.left < maxLeft ? virtual.left : maxLeft,
+                                            width: pageWidth - virtual.left
+                                        });
+                                    }
+                                }else{
+                                    myElement.set({ 
+                                        left:  0,
+                                        width: virtual.width + virtual.left
+                                    });
+                                }
+
+
+                            }else{
+
+
+                                virtual[changeOne] += diffY;
+                                if(changeOne === 'top'){
+                                    virtual.height -= diffY;
+                                }
+                                if(virtual.top >= 0){
+                                    myElement.set({ 
+                                        top:    virtual.top < maxTop ? virtual.top : maxTop,
+                                        height: virtual.height
+                                    });
+                                }else{
+                                    myElement.set({ 
+                                        top:  0,
+                                        height: virtual.height + virtual.top
+                                    });
+                                }
+
+
+                            }
+                            
+
+                        }
+                        
+                        
+                        startX = evt.pageX;
+                        startY = evt.pageY;
+
+                        selection.do('updateDimensions');
+                    
+                        evt.stopPropagation();
+                        evt.preventDefault();
+                        
+                    };
+
+
+
+                // finally: register mousedown eventhandler for all my handles
+
+                for(var i = 0, l = config.allResizeHandles.length; i < l; i++){
+                    config.allResizeHandles[i].addEventListener('mousedown', onMouseDown, false);
+                }
+
+
+
+
+
+
+            }
+        }
+
+
+    }
+
+
+}});

+ 138 - 0
src/allplatforms/classes/SCGUI.js

@@ -0,0 +1,138 @@
+SC.loadPackage({ 'SCGUI': {
+
+
+    comment: 'I provide help constructing a GUI. I am made to be used as a mixin to other classes, or as storage for static methods.',
+
+    properties: {
+        mySCGUIWindow: { comment: 'I store a reference to the standard window.' }
+    },
+
+    methods: {
+
+
+        createElement: {
+            comment: 'I create an GUI element. I expect an object as argument like this: \n{\n'+
+                '\ttype:   type of HTMLElement to create (aString)\n'+
+                '\twindow: window object (optional)\n'+
+                '\tparent: parent to append to (optional, aString)\n'+
+                '\tid:     ID of the element (optional, aString)\n'+
+                '\tattr:   attributes of the element (optional, object of key-value-pairs)\n'+
+                '\tinner:  string for innerHTML (optional, aString)\n'+
+                '}',
+            code: function (arg){
+                var elementToCreate = document.createElement(arg.type);
+                
+                for(var key in arg.attr){
+                    elementToCreate.setAttribute(key, arg.attr[key]);
+                }
+                if(arg.id)      elementToCreate.setAttribute('id', arg.id);
+                if(arg.inner)   elementToCreate.innerHTML = arg.inner;
+                if(arg.parent)  {
+                    var window = arg.window || this.get('mySCGUIWindow') || window;
+                    var parent = window.document.getElementById(arg.parent);
+                    return parent.appendChild(elementToCreate);
+                }else{
+                    return elementToCreate;
+                }
+                
+            }
+        },
+
+        getElement: {
+            comment: 'I get an element. I expect an object as argument like this: \n{\n'+
+            '\tid:      element to work on\n'+
+            '\twindow:  window object (optional)\n'+
+            '}',
+            code: function(arg){
+                var window = arg.window || this.get('mySCGUIWindow') || window;
+                return window.document.getElementById(arg.id);
+            }
+        },
+
+        setAttribute: {
+            comment: 'I set the attributes of an element. I expect an object as argument like this: \n{\n'+
+            '\tid:      element to work on\n'+
+            '\twindow:  window object (optional)\n'+
+            '\tattr:    key-value-pairs of the attributes\n'+
+            '}',
+            code: function(arg){
+                var window = arg.window || this.get('mySCGUIWindow') || window;
+                for(key in arg.attr){
+                    window.document.getElementById(arg.id).setAttribute(key, arg.attr[key]);
+                }
+            }
+        },
+
+        addListener: {
+            comment: 'I add an eventListener to an element. I expect an object as argument like this: \n{\n'+
+            '\tid:       element to work on\n'+
+            '\twindow:   window object (optional)\n'+
+            '\tevent:    name of the event\n'+
+            '\tcallback: the callback function for the event\n'+
+            '\tbubbling: true|false\n'+
+            '}',
+            code: function(arg){
+                var window = arg.window || this.get('mySCGUIWindow') || window,
+                    self   = this;
+                window.document.getElementById(arg.id).addEventListener(
+                    arg.event,
+                    function(){
+                        arg.callback.apply(self, arguments);
+                    },
+                    arg.bubbling
+                );
+            }
+        },
+
+        showElement: {
+            comment: 'I make an element visible. I expect an object as argument like this: \n{\n'+
+            '\tid:     element to show\n'+
+            '\twindow: window object (optional)\n'+
+            '}',
+            code: function(arg){
+                var window = arg.window || this.get('mySCGUIWindow') || window;
+                window.document.getElementById(arg.id).style.visibility = 'visible';
+            }
+        },
+
+        hideElement: {
+            comment: 'I make an element invisible. I expect an object as argument like this: \n{\n'+
+            '\tid:     element to show\n'+
+            '\twindow: window object (optional)\n'+
+            '}',
+            code: function(arg){
+                var window = arg.window || this.get('mySCGUIWindow') || window;
+                window.document.getElementById(arg.id).style.visibility = 'hidden';
+            }
+        },
+
+        emptyElement: {
+            comment: 'I empyt the innerHTML of an element. I expect an object as argument like this: \n{\n'+
+            '\tid:     element to be emptied\n'+
+            '\twindow: window object (optional)\n'+
+            '}',
+            code: function(arg){
+                var window = arg.window || this.get('mySCGUIWindow') || window;
+                window.document.getElementById(arg.id).innerHTML = '';
+            }
+        },
+
+        deselectOptions: {
+            comment: 'I deselect all options in a select box. I expect an object as argument like this: \n{\n'+
+            '\tid:     select box element\n'+
+            '\twindow: window object (optional)\n'+
+            '}',
+            code: function(arg){
+                var window = arg.window || this.get('mySCGUIWindow') || window;
+                var select = window.document.getElementById(arg.id);
+                for(var i = 0; i < select.options.length; i++){
+                    select.options[i].selected = false;
+                }
+            }
+        }
+
+
+    }
+
+
+}});

+ 505 - 0
src/allplatforms/classes/SCSystemBrowser.js

@@ -0,0 +1,505 @@
+SC.loadPackage({ 'SCSystemBrowser': {
+
+
+    comment: 'I am the SmallClasses.js System Browser, and part of the SmallClasses.js project',
+
+    mixins: [ 'SCGUI' ],
+
+    sharedProperties: {
+
+        windowFile:   { comment:   'This file contains the html for the SCSystemBrowser\'s GUI. It needs to be available under the same domain, with a path relative to the embbeding html file.',
+                        initValue: '/resources/dev/SCSystemBrowser.html' },
+        windowConfig: { comment:   'This is the configuration for the window.open() function.',
+                        initValue: 'height=750, width=1024, titlebar=no, location=no, menubar=no, resizable=yes, status=no, toolbar=no' }
+
+    },
+
+    properties: {
+        
+        browserWindow: {
+            comment:   'I store the reference to the SystemBrowser\'s window',
+            transform: function(aBrowserWindow){
+                this.set({ 
+                    mySCGUIWindow:  aBrowserWindow,
+                    rootWindow:     aBrowserWindow.opener
+                });
+                return aBrowserWindow
+            }
+        },
+        rootWindow: {
+            comment:    'I store the reference to the root window which opened the SystemBrowser'
+        },
+
+        currentClass: {},
+        currentClassMember: {},
+        currentEditorMode: {}
+
+    },
+
+    methods: {
+
+
+        openSystemBrowser: {
+            comment: 'I open a window for a new SCSystemBrowser. Static Method!',
+            code:    function(){
+
+                window.open(
+                    SC.getSharedProperty('SCSystemBrowser', 'windowFile'),
+                    '_blank',
+                    SC.getSharedProperty('SCSystemBrowser', 'windowConfig')
+                )
+                
+            }
+        },
+
+        init: {
+            comment: 'I init myself. I am called from the newly created window!\narguments: { browserWindow: <the window containing me.> }',
+            code:    function (arg){
+
+                this.set({ 
+                    browserWindow:      arg.browserWindow,
+                    currentClass:       '',
+                    currentClassMember: {
+                        type:  '',
+                        value: ''
+                    },
+                    currentEditorMode:  ''
+                });
+
+                this.do('initClassListView');
+                this.do('updateClassListView');
+
+                
+                this.do('initTraitsView');
+                this.do('initMixinsView');
+                this.do('initSharedPropertiesView');
+                this.do('initPropertiesView');
+                this.do('initMethodsView');
+
+                //if(this.get('rootWindow')['$$TEMP_SCSystemBrowser']){ ... currentClass ; updateClassView }
+                
+                
+            }
+
+        },
+
+        updateClassView: {
+            comment: 'I update the view on the right side, after a class selection',
+            code:    function(arg){
+
+                this.do('updateTraitsView');
+                this.do('updateMixinsView');
+                this.do('updateSharedPropertiesView');
+                this.do('updatePropertiesView');
+                this.do('updateMethodsView');
+
+                this.do('updateClassCommentEditor')
+
+            }
+        },
+
+        
+        initClassListView: {
+            comment: 'I init the the class list view',
+            code:    function(arg){
+
+                var classHasBeenChoosen = function(evt){
+                    this.set({ currentClass: evt.target.value })
+                    this.do('updateClassView');
+
+                    this.do('showElement', { id: 'classEditor' });
+                    this.do('hideElement', { id: 'sharedPropertyEditor' });
+                    this.do('hideElement', { id: 'propertyEditor' });
+                    this.do('hideElement', { id: 'methodEditor' });
+                };
+
+                this.do('addListener', { 
+                    id:       'packageListSelect',
+                    event:    'click',
+                    bubbling: false,
+                    callback: classHasBeenChoosen
+                });
+
+                this.do('addListener', { 
+                    id:       'packageListSelect',
+                    event:    'change',
+                    bubbling: false,
+                    callback: classHasBeenChoosen
+                });
+                
+            }
+
+        },
+
+        updateClassListView: {
+            comment: 'I update the the class list view',
+            code:    function(arg){
+
+                var classList = SC.getClasses();
+                classList.sort(function (a, b) {
+                    if (a > b) return 1;
+                    if (a < b) return -1;
+                    return 0;
+                });
+
+                this.do('emptyElement', { id: 'packageListSelect' });
+                this.do('setAttribute', { id: 'packageListSelect', 
+                                          attr: { size: classList.length } });
+                for(var i = 0; i < classList.length; i++){
+                    this.do('createElement', {
+                        type:   'option',
+                        parent: 'packageListSelect',
+                        inner:  classList[i],
+                        attr:   { name: classList[i] }
+                    });
+                }
+                
+            }
+
+        },
+
+
+        updateTraitsView: {
+            comment: 'I update the the traits view',
+            code:    function(arg){
+
+                var traits = SC.getTraits( this.get('currentClass') );
+                traits = (traits.length == 0) ? '' : JSON.stringify(traits);
+
+                
+                this.do('setAttribute', { id: 'classTraitsInput',
+                                          attr: { value: traits }
+                                        });
+                
+            }
+
+        },
+
+
+        updateMixinsView: {
+            comment: 'I update the the mixins view',
+            code:    function(arg){
+
+                var mixins = SC.getMixins( this.get('currentClass') );
+                mixins = (mixins.length == 0) ? '' : JSON.stringify(mixins);
+
+                
+                this.do('setAttribute', { id: 'classMixinsInput',
+                                          attr: { value: mixins } });
+                
+            }
+
+        },
+
+        updateSharedPropertiesView: {
+            comment: 'I update the the shared properties view',
+            code:    function(arg){
+
+                var sharedProperties = SC.getSharedProperties( this.get('currentClass') );
+                sharedProperties.sort(function (a, b) {
+                    if (a > b) return 1;
+                    if (a < b) return -1;
+                    return 0;
+                });
+
+                this.do('emptyElement', { id: 'classSharedPropertiesSelect' });
+                this.do('setAttribute', { id: 'classSharedPropertiesSelect', 
+                                          attr: { size: (sharedProperties.length > 1 ? sharedProperties.length : 2) } });
+                for(var i = 0; i < sharedProperties.length; i++){
+                    this.do('createElement', {
+                        type:   'option',
+                        parent: 'classSharedPropertiesSelect',
+                        inner:  sharedProperties[i],
+                        attr:   { name: sharedProperties[i] }
+                    });
+                }
+                
+            }
+
+        },
+
+        updatePropertiesView: {
+            comment: 'I update the the  properties view',
+            code:    function(arg){
+
+                var properties = SC.getProperties( this.get('currentClass') );
+                properties.sort(function (a, b) {
+                    if (a > b) return 1;
+                    if (a < b) return -1;
+                    return 0;
+                });
+
+                this.do('emptyElement', { id: 'classPropertiesSelect' });
+                this.do('setAttribute', { id: 'classPropertiesSelect', 
+                                          attr: { size: (properties.length > 1 ? properties.length : 2) } });
+                for(var i = 0; i < properties.length; i++){
+                    this.do('createElement', {
+                        type:   'option',
+                        parent: 'classPropertiesSelect',
+                        inner:  properties[i],
+                        attr:   { name: properties[i] }
+                    });
+                }
+                
+            }
+
+        },
+
+        updateMethodsView: {
+            comment: 'I update the the method view',
+            code:    function(arg){
+
+                var methods = SC.getMethods( this.get('currentClass') );
+                methods.sort(function (a, b) {
+                    if (a > b) return 1;
+                    if (a < b) return -1;
+                    return 0;
+                });
+
+                this.do('emptyElement', { id: 'classMethodsSelect' });
+                this.do('setAttribute', { id: 'classMethodsSelect', 
+                                          attr: { size: (methods.length > 1 ? methods.length : 2) } });
+                for(var i = 0; i < methods.length; i++){
+                    this.do('createElement', {
+                        type:   'option',
+                        parent: 'classMethodsSelect',
+                        inner:  methods[i],
+                        attr:   { name: methods[i] }
+                    });
+                }
+                
+            }
+
+        },
+
+        initSharedPropertiesView: {
+            comment: 'I init the the shared properties view',
+            code:    function(arg){
+
+                this.do('addListener', { 
+                    id:       'classSharedPropertiesSelect',
+                    event:    'change',
+                    bubbling: false,
+                    callback: function(evt){
+                        this.set({ currentClassMember: {
+                            type:  'sharedProperty',
+                            value: evt.target.value
+                        } });
+                        this.do('deselectOptions', { id: 'classPropertiesSelect' });
+                        this.do('deselectOptions', { id: 'classMethodsSelect' });
+
+                        this.do('updateSharedPropertyEditor');
+                        
+                        this.do('hideElement', { id: 'classEditor' });
+                        this.do('hideElement', { id: 'propertyEditor' });
+                        this.do('hideElement', { id: 'methodEditor' });
+                        this.do('showElement', { id: 'sharedPropertyEditor' });
+                    }
+                });
+                
+            }
+
+        },
+
+        initPropertiesView: {
+            comment: 'I init the the properties view',
+            code:    function(arg){
+
+                this.do('addListener', { 
+                    id:       'classPropertiesSelect',
+                    event:    'change',
+                    bubbling: false,
+                    callback: function(evt){
+                        this.set({ currentClassMember: {
+                            type:  'property',
+                            value: evt.target.value
+                        } });
+                        this.do('deselectOptions', { id: 'classSharedPropertiesSelect' });
+                        this.do('deselectOptions', { id: 'classMethodsSelect' });
+
+                        this.do('updatePropertyEditor');
+
+                        this.do('hideElement', { id: 'classEditor' });
+                        this.do('hideElement', { id: 'sharedPropertyEditor' });
+                        this.do('hideElement', { id: 'methodEditor' });
+                        this.do('showElement', { id: 'propertyEditor' });
+                    }
+                });
+                
+            }
+
+        },
+
+        initMethodsView: {
+            comment: 'I init the the method view',
+            code:    function(arg){
+
+                this.do('addListener', { 
+                    id:       'classMethodsSelect',
+                    event:    'change',
+                    bubbling: false,
+                    callback: function(evt){
+                        this.set({ currentClassMember: {
+                            type:  'method',
+                            value: evt.target.value
+                        } });
+                        this.do('deselectOptions', { id: 'classSharedPropertiesSelect' });
+                        this.do('deselectOptions', { id: 'classPropertiesSelect' });
+
+                        this.do('updateMethodEditor');
+
+                        this.do('hideElement', { id: 'classEditor' });
+                        this.do('hideElement', { id: 'sharedPropertyEditor' });
+                        this.do('hideElement', { id: 'propertyEditor' });
+                        this.do('showElement', { id: 'methodEditor' });
+                    }
+                });
+                
+            }
+
+        },
+
+        updateClassCommentEditor: {
+            comment: 'I update the the class comment editor',
+            code:    function(arg){
+
+                this.do('getElement', { id: 'classCommentEditor'}).innerHTML = this.do('prettyPrint',
+                    SC.getClassComment( this.get('currentClass') )
+                );
+
+            }
+        },
+
+        updateSharedPropertyEditor: {
+            comment: 'I update the the shared property editor',
+            code:    function(arg){
+
+                if( this.get('currentClassMember').type == 'sharedProperty' ){
+
+                    this.do('getElement', { id: 'sharedPropertyCommentEditor'}).innerHTML = this.do('prettyPrint',
+                        SC.getSharedPropertyComment( 
+                            this.get('currentClass'),
+                            this.get('currentClassMember').value
+                        )
+                    );
+
+                    var currentValue = SC.getSharedProperty( 
+                            this.get('currentClass'),
+                            this.get('currentClassMember').value
+                        );
+                    try{
+                        currentValue = JSON.stringify( currentValue );
+                    }catch(e){
+                        currentValue = currentValue.toString();
+                    }
+                    this.do('getElement', { id: 'sharedPropertyInitValueEditor'}).innerHTML = this.do('prettyPrint',
+                        currentValue
+                    );
+
+
+                    this.get('browserWindow').editorSharedPropertyValidator.setValue(
+                        SC.getSharedPropertyValidator( 
+                            this.get('currentClass'),
+                            this.get('currentClassMember').value
+                        ).toString()
+                        , -1
+                    );
+                    this.get('browserWindow').editorSharedPropertyValidator.resize(true);
+                    
+                    this.get('browserWindow').editorSharedPropertyTransformer.setValue(
+                        SC.getSharedPropertyTransformer( 
+                            this.get('currentClass'),
+                            this.get('currentClassMember').value
+                        ).toString()
+                        , -1
+                    );
+                    this.get('browserWindow').editorPropertyTransformer.resize(true);
+
+
+                }
+
+            }
+        },
+
+        updatePropertyEditor: {
+            comment: 'I update the the property editor',
+            code:    function(arg){
+
+                if( this.get('currentClassMember').type == 'property' ){
+
+                    this.do('getElement', { id: 'propertyCommentEditor'}).innerHTML = this.do('prettyPrint',
+                        SC.getPropertyComment( 
+                            this.get('currentClass'),
+                            this.get('currentClassMember').value
+                        )
+                    );
+                    
+                    this.get('browserWindow').editorPropertyValidator.setValue(
+                        SC.getPropertyValidator( 
+                            this.get('currentClass'),
+                            this.get('currentClassMember').value
+                        ).toString()
+                        , -1
+                    );
+                    this.get('browserWindow').editorPropertyValidator.resize(true);
+
+                    this.get('browserWindow').editorPropertyTransformer.setValue(
+                        SC.getPropertyTransformer( 
+                            this.get('currentClass'),
+                            this.get('currentClassMember').value
+                        ).toString()
+                        , -1
+                    );
+                    this.get('browserWindow').editorPropertyTransformer.resize(true);
+
+                }
+
+            }
+        },
+
+        updateMethodEditor: {
+            comment: 'I update the the method editor',
+            code:    function(arg){
+
+                if( this.get('currentClassMember').type == 'method' ){
+
+                    this.do('getElement', { id: 'methodCommentEditor'}).innerHTML = this.do('prettyPrint',
+                        SC.getMethodComment( 
+                            this.get('currentClass'),
+                            this.get('currentClassMember').value
+                        )
+                    );
+                    
+                    this.get('browserWindow').editorMethodCode.setValue(
+                        SC.getMethodCode( 
+                            this.get('currentClass'),
+                            this.get('currentClassMember').value
+                        ).toString()
+                        , -1
+                    );
+                    this.get('browserWindow').editorMethodCode.resize(true);
+
+
+                }
+
+            }
+        },
+
+
+        prettyPrint: {
+            comment: 'I parse a string for special chars. (TEMPORARY)',
+            code: function (arg){
+                var str = arg;
+                return   str.replace(/</g, '&lt;')
+                            .replace(/>/g, '&gt;')
+                            .replace(/\n/g, '<br>')
+                            .replace(/\t/g, '&nbsp;&nbsp;&nbsp;&nbsp;');
+
+            }
+        }
+
+
+
+    }
+
+}});

+ 705 - 0
src/allplatforms/classes/Selection.js

@@ -0,0 +1,705 @@
+SC.loadPackage({ 'Selection': {
+
+    comment: 'I represent the selection of the editing tool, holding one or more Elements. I selection can move its Elements, it shows the Widgets, that apply to all of the Elements in the current selection, and it can start the content editing mode of one of its Elements.',
+
+    sharedProperties: {
+        selectionTools: { initValue: '<div class="sg-editing-selection"><div class="sg-editing-selection-widget-menu-right"></div><div class="sg-editing-selection-widget-menu-bottom"></div></div>' },
+    },
+
+    properties: {
+
+        node:       { comment: 'My DOM node.' },
+        elements:   { comment: 'I hold an array of the currently selected elements.' },
+        active:     { comment: 'Wether a selection is active or not.' },
+
+        widgetsRight:    { comment: 'I hold an array of the standard widgets, which are applicable to all elements, and which are shown on the right side of the selection.'},
+        widgetsBottom:   { comment: 'I hold an array of the widgets, which are applicable to the elements in the current selection, and which are shown on the bottom of the selection.'},
+        menuNodeRight:   { comment: 'I hold the DOM node containing the right menu node.'},
+        menuNodeBottom:  { comment: 'I hold the DOM node containing the bottom menu node.'},
+
+        activeWidget:    { comment: 'I store either null or the active (selected) widget.'},
+
+        lockWidget:      { comment: 'My lockWidget plays a special role, since it appears only on multiple selections and must update its state.'}
+
+    },
+
+    methods: {
+
+        init: { 
+            comment:    'I init myself.',
+            code:       function(){
+
+                var node = (new DOMParser()).parseFromString(this.class.get('selectionTools'), 'text/html').body.firstChild;
+
+                this.set({ 
+                    node:            node,
+                    menuNodeRight:   node.firstChild,
+                    menuNodeBottom:  node.lastChild,
+                    widgetsRight:    [],
+                    widgetsBottom:   [],
+                    active:          false,
+                    elements:        [],
+                    activeWidget:    null
+                });
+
+                var generalWidgets          = ['WidgetLayerTop', 'WidgetLayerBottom', 'WidgetEditHTML', 'WidgetCopy', 'WidgetDelete'],
+                    generalWidgetsContainer = this.get('menuNodeRight'),
+                    widget = null;
+
+                for(var i = 0, l = generalWidgets.length; i < l; i++){
+                    widget = SC.init(generalWidgets[i], this);
+                    this.get('widgetsRight').push(widget);
+                    generalWidgetsContainer.appendChild(widget.get('widgetMenu'));
+                }
+
+
+                this.set({ lockWidget: SC.init('WidgetLock', this) });
+                    
+
+
+            }
+        },
+
+        updateWidgetMenu: { 
+            comment:    'I update the menu of widgets according to the current selection.',
+            code:       function(){
+
+                // Update widget menu to the bottom
+
+                var elements                = this.get('elements'),
+                    elementsWidgetSets      = [],
+                    currentWidgets          = [],
+                    currentWidgetsClasses   = null,
+                    currentWidgetsContainer = this.get('menuNodeBottom'),
+                    widget = null;
+
+
+                while(currentWidgetsContainer.firstChild){
+                    currentWidgetsContainer.removeChild(currentWidgetsContainer.firstChild);
+                }
+
+                if(elements.length > 0){
+                    
+                    // find intersection of applicable widgets
+                    for(var i = 0, l = elements.length; i < l; i++){
+                        elementsWidgetSets.push( elements[i].class.get('applicableWidgets') );
+                    }
+                    
+                    if(elementsWidgetSets.length > 1){
+                        currentWidgetsClasses = elementsWidgetSets.shift().filter(function(v) {
+                            return elementsWidgetSets.every(function(a) {
+                                return a.indexOf(v) !== -1;
+                            });
+                        });
+                    }else{
+                        currentWidgetsClasses = elementsWidgetSets[0];
+                    }
+
+                    for(var i = 0, l = currentWidgetsClasses.length; i < l; i++){
+                        widget = SC.init(currentWidgetsClasses[i], this);
+                        currentWidgets.push(widget);
+                        currentWidgetsContainer.appendChild(widget.get('widgetMenu'));
+                    }
+
+                }
+
+                this.set({ 
+                    widgetsBottom: currentWidgets,
+                    activeWidget:  null
+                });
+
+
+
+                // Update widget to the right
+
+                this.do('updateLockGroup');
+
+
+
+                // Finally draw red outline when multiple elements are selected
+                if(elements.length === 1){
+                    this.get('node').classList.remove('sg-editing-selection-outline');
+                }else{
+                    this.get('node').classList.add('sg-editing-selection-outline');
+                }
+
+            }
+        },
+
+
+        updateLockGroup: {
+
+            comment:    'After adding and removing elements, I check wether I have multiple elements, and if so, if they form a locked group or not.',
+            code:       function(){
+
+                var myElements    = this.get('elements'),
+                    lockWidget    = this.get('lockWidget'),
+                    menuNodeRight = this.get('menuNodeRight'),
+                    isGroup       = true;
+
+                if(myElements.length > 1){
+
+                    // check and add lockWidget
+                    if(lockWidget.get('widgetMenu').parentNode !== menuNodeRight){
+                        menuNodeRight.insertBefore(lockWidget.get('widgetMenu'), menuNodeRight.childNodes[0]);
+                    }
+
+                    // check group status of myElements
+                    for(var i = 0, l = myElements.length; i < l; i++){
+                        isGroup = isGroup && (myElements[i].get('group') !== null);
+                    }
+                    if(isGroup){
+                        for(var i = 1, l = myElements.length; i < l; i++){
+                            isGroup = isGroup && (myElements[i].get('group') === myElements[i-1].get('group'))
+                        }
+                    }
+                    lockWidget.set({ locked: isGroup })
+
+
+                }else if(myElements.length > 0){
+
+                    // remove lockWidget
+                    if(lockWidget.get('widgetMenu').parentNode === menuNodeRight){
+                        menuNodeRight.removeChild(lockWidget.get('widgetMenu'));
+                    }
+
+                }
+
+
+            }
+
+        },
+
+        
+
+        addElement: { 
+            comment:    'I add anElement to myself.',
+            code:       function(anElement){
+
+                var elementsToAdd = [ anElement ],
+                    myElements    = this.get('elements'),
+                    group         = anElement.get('group');
+
+                if(group !== null){
+
+                    for(var elements = SuperGlue.get('document').get('children'),
+                            i = 0, l = elements.length; i < l; i++){
+
+                        if(elements[i].get('group') === group){
+                            elementsToAdd.push(elements[i]);
+                        }
+
+                    }
+
+                }
+
+                for(var i = 0, l = elementsToAdd.length; i < l; i++){
+                    if(myElements.indexOf(elementsToAdd[i]) < 0){
+                        myElements.push(elementsToAdd[i]);
+
+                    }
+                }
+
+                
+                if(myElements.length === 1){
+                    myElements[0].get('resizeHandles').set({ selected: true });
+                    myElements[0].get('resizeHandles').do('showResizeHandles');
+                }else{
+                    for(var i = 0, l = myElements.length; i < l; i++){
+                        myElements[i].get('resizeHandles').set({ selected: false });
+                    }
+                }
+
+                if(!this.get('active')){
+                    SuperGlue.get('document').get('editingContainer').appendChild(this.get('node'));
+                }
+                this.do('updateDimensions');
+                this.do('updateWidgetMenu');
+
+                
+
+            }
+        },
+
+        removeElement: { 
+            comment:    'I remove anElement from myself.',
+            code:       function(anElement){
+
+                var myElements       = this.get('elements'),
+                    elementsToRemove = [ anElement ],
+                    group            = anElement.get('group');
+
+                if(group !== null){
+
+                    for(var i = 0, l = myElements.length; i < l; i++){
+
+                        if(myElements[i].get('group') === group){
+                            elementsToRemove.push(myElements[i]);
+                        }
+
+                    }
+
+                }
+
+                for(var i = 0, l = elementsToRemove.length; i < l; i++){
+                    var elementIndex = myElements.indexOf(elementsToRemove[i]);
+                    if(elementIndex >= 0){
+                        myElements.splice(elementIndex, 1);
+                        elementsToRemove[i].get('resizeHandles').set({ selected: false, mouseOnElement: false });
+                        elementsToRemove[i].get('resizeHandles').do('hideResizeHandles', true);
+                    }
+                }
+
+                
+                
+                if(myElements.length === 0){
+                    SuperGlue.get('document').get('editingContainer').removeChild(this.get('node'));
+                    this.set({ active: false });
+                }else{
+
+                    if(myElements.length === 1){
+                        myElements[0].get('resizeHandles').set({ selected: true });
+                        myElements[0].get('resizeHandles').do('showResizeHandles');
+                    }else{
+                        for(var i = 0, l = myElements.length; i < l; i++){
+                            myElements[i].get('resizeHandles').set({ selected: false });
+                        }
+                    }
+
+                    this.do('updateDimensions');
+                    this.do('updateWidgetMenu');
+                }
+
+
+
+            }
+        },
+
+        toggleSelectionFor: { 
+            comment:    'I check wether anElement belongs to me already, and then add or remove it.',
+            code:       function(anElement){
+
+                if(this.get('elements').indexOf(anElement) > -1){
+                    this.do('removeElement', anElement);
+                }else{
+                    this.do('addElement', anElement);
+                }
+
+            }
+        },
+
+        clearAll: { 
+            comment:    'I remove all my Elements.',
+            code:       function(){
+
+                var elements = this.get('elements');
+                for(var i = 0, l = elements.length; i < l; i++){
+                    elements[i].get('resizeHandles').set({ selected: false, mouseOnElement: false });
+                    elements[i].get('resizeHandles').do('hideResizeHandles', true)
+                }
+
+                var editingContainer = SuperGlue.get('document').get('editingContainer');
+                if(this.get('node').parentNode === editingContainer){
+                    editingContainer.removeChild(this.get('node'));
+                }
+
+                this.set({
+                    elements: [],
+                    active: false
+                });
+                
+
+            }
+        },
+
+        isEmpty: { 
+            comment:    'Am I empty?',
+            code:       function(){
+                return this.get('elements').length === 0
+            }
+        },
+
+        calculateDimensions: { 
+            comment:    'I return the overall dimension to fit in all my Elements in the form [top, left, width, height].',
+            code:       function(){
+
+                if(this.do('isEmpty')){ return []; }
+                
+                var myElements = this.get('elements'),
+                    top        = myElements[0].get('top'),
+                    left       = myElements[0].get('left'),
+                    width      = 0,
+                    height     = 0,
+                    l          = myElements.length;
+                for(var i = 0; i < l; i++){
+                    top        = myElements[i].get('top') < top ? myElements[i].get('top') : top;
+                    left       = myElements[i].get('left') < left ? myElements[i].get('left') : left;
+                    width      = (myElements[i].get('width') + myElements[i].get('left')) < width 
+                                  ? width
+                                  : (myElements[i].get('width') + myElements[i].get('left'));
+                    height     = (myElements[i].get('height') + myElements[i].get('top')) < height 
+                                  ? height 
+                                  : (myElements[i].get('height') + myElements[i].get('top'));
+                }
+                width -= left;
+                height -= top;
+                
+                return [top, left, width, height];
+
+            }
+        },
+
+        updateDimensions: { 
+            comment:    'I update the overall dimension of my DOM node to fit in all my Elements.',
+            code:       function(){
+
+                if(this.do('isEmpty')){ return; }
+                
+                var nodeStyle  = this.get('node').style,
+                    dimensions = this.do('calculateDimensions'),
+                    top        = dimensions[0],
+                    left       = dimensions[1],
+                    width      = dimensions[2],
+                    height     = dimensions[3];
+                
+                nodeStyle.top = top + 'px';
+                nodeStyle.left = left + 'px';
+                nodeStyle.width = width + 'px';
+                nodeStyle.height = height + 'px';
+
+            }
+        },
+
+
+
+
+
+        registerForSelection: { 
+            comment:    'I prepare an Element to be selectable. Used by Element>>init.',
+            code:       function(anElement){
+
+
+
+                var self           = this,
+                    myNode         = this.get('node'),
+                    thisElement             = anElement,
+                    elementNode             = anElement.get('node'),
+                    elements                = [],
+                    
+                    infiniteSpace  = true,
+                    pageWidth      = 0,
+                    virtualTop     = 0,
+                    virtualLeft    = 0,
+                    virtualWidth   = 0,
+                    virtualHeight  = 0,
+
+                    elementsOffsetsX = null,
+                    elementsOffsetsY = null,
+
+                    targetNotInSelection,
+
+                    isGroup,
+                    group,
+
+                    startX         = 0,
+                    startY         = 0,
+
+                    clickPrecisionXleft   = 0,
+                    clickPrecisionXright  = 0,
+                    clickPrecisionYtop    = 0,
+                    clickPrecisionYbottom = 0,
+                    withinClickPrecision  = true,
+
+                    widthMarkersVisible,
+                    gridVisible,
+
+                    onMouseDown = function(evt){
+
+                        if(evt.button !== 0) return;
+
+                        if(evt.shiftKey || evt.ctrlKey){
+
+                            document.addEventListener('mouseup', onMouseUpWithModifier, true);
+                            SuperGlue.get('document').set({ interactionInProgress: true });
+                            // UNDO
+
+                        }else{
+
+                            startX   = evt.pageX;
+                            startY   = evt.pageY;
+                            
+                            clickPrecisionXleft   = startX - 6;
+                            clickPrecisionXright  = startX + 6;
+                            clickPrecisionYtop    = startY - 6;
+                            clickPrecisionYbottom = startY + 6;
+                            withinClickPrecision  = true;
+
+                            isGroup = false;
+
+                            infiniteSpace = ! SuperGlue.get('document').get('layout').centered;
+                            pageWidth     = SuperGlue.get('document').get('layout').width;
+                            if(SuperGlue.get('document').get('grid').get('active')){
+                                var gridSize = SuperGlue.get('document').get('grid').get('gridSize');
+                                pageWidth = Math.floor(pageWidth / gridSize) * gridSize;
+                            }
+
+
+                            elements = self.get('elements');
+                            
+                            if(elements.length !== 0){
+
+                                targetNotInSelection = true;
+                                for(var i = 0, l = elements.length; i < l; i++){
+                                    if(elements[i].get('node') === thisElement.get('node')){
+                                        targetNotInSelection = false;
+                                        break;
+                                    }
+                                }
+                                if(targetNotInSelection){
+                                    self.do('clearAll');
+                                    elements = self.get('elements');
+                                }
+
+                            }else{
+                                targetNotInSelection = elements.indexOf(thisElement) < 0;
+                            }
+
+                            if(elements.length === 0){
+
+                                group = thisElement.get('group');
+
+                                if(group !== null){
+
+                                    isGroup = true;
+
+                                    for(var allElements = SuperGlue.get('document').get('children'),
+                                            i = 0, l = allElements.length; i < l; i++){
+
+                                        if(allElements[i].get('group') === group){
+                                            elements.push(allElements[i]);
+                                        }
+
+                                    }
+
+                                    
+
+                                }
+
+                            }
+
+                            if(elements.length === 0){
+
+                                virtualTop    = anElement.get('top');
+                                virtualLeft   = anElement.get('left');
+                                virtualWidth  = anElement.get('width');
+                                virtualHeight = anElement.get('height');
+
+                            }else{
+
+                                var dimensions = self.do('calculateDimensions');
+                                virtualTop     = dimensions[0];
+                                virtualLeft    = dimensions[1];
+                                virtualWidth   = dimensions[2];
+                                virtualHeight  = dimensions[3];
+
+                                elementsOffsetsX = [];
+                                elementsOffsetsY = [];
+                                for(var i = 0, l = elements.length; i < l; i++){
+                                    elementsOffsetsX.push( elements[i].get('left') - virtualLeft );
+                                    elementsOffsetsY.push( elements[i].get('top')  - virtualTop  );
+                                }
+
+                            }
+
+                            
+                            document.addEventListener('mousemove', onMouseMove, true);
+                            document.addEventListener('mouseup',   onMouseUp,   true);
+                            SuperGlue.get('document').set({ interactionInProgress: true });
+                            // UNDO
+
+                        }
+
+                        evt.stopPropagation();
+                        evt.preventDefault();
+
+
+                    },
+
+                    onMouseUpWithModifier = function(evt){
+
+                        self.do('toggleSelectionFor', thisElement);
+                        document.removeEventListener('mouseup', onMouseUpWithModifier, true);
+                        SuperGlue.get('document').set({ interactionInProgress: false });
+                        
+                        // UNDO
+                        
+                        evt.stopPropagation();
+                        evt.preventDefault();
+                    },
+
+                    onMouseUp = function(evt){
+
+                        document.removeEventListener('mousemove', onMouseMove, true);
+                        document.removeEventListener('mouseup',   onMouseUp,   true);
+                        
+                        var myDocument = SuperGlue.get('document');
+                        myDocument.set({ interactionInProgress: false });
+                        myDocument.do('afterLayoutHasChanged');
+
+
+                        if(withinClickPrecision){
+                            
+                            if(     !targetNotInSelection
+                                &&  thisElement.class() === 'TextElement'
+                            ){
+
+                                thisElement.do('activateTextEditor');
+
+                            }else{
+                                
+                                self.do('clearAll');
+                                self.do('addElement', thisElement);
+
+                            }
+                            
+
+                        }else{
+
+                            SuperGlue.get('document').get('widthMarkers').set({ visible: widthMarkersVisible });
+                            SuperGlue.get('document').get('grid').set({ visible: gridVisible });
+
+                            if(isGroup){
+                                self.do('clearAll');
+                            }
+                        }
+
+
+                        // UNDO
+                        
+                        evt.stopPropagation();
+                        evt.preventDefault();
+
+                    },
+
+                    onMouseMove = function(evt){
+
+                        var diffX = evt.pageX - startX,
+                            diffY = evt.pageY - startY;
+
+                        
+
+                        if( withinClickPrecision &&
+                            (    evt.pageX < clickPrecisionXleft
+                              || evt.pageX > clickPrecisionXright
+                              || evt.pageY < clickPrecisionYtop
+                              || evt.pageY > clickPrecisionYbottom )
+                        ){
+                            var widthMarkers = SuperGlue.get('document').get('widthMarkers'),
+                                grid         = SuperGlue.get('document').get('grid');
+                            widthMarkersVisible = widthMarkers.get('visible');
+                            gridVisible         = grid.get('visible');
+                            widthMarkers.set({ visible: true });
+                            grid.set({ visible: true });
+
+                            withinClickPrecision = false;
+                        }
+
+
+                        if(!withinClickPrecision){
+
+
+                            virtualTop  += diffY;
+                            virtualLeft += diffX;
+
+
+                            if(elements.length === 0){
+
+                                if(virtualTop > 0){
+                                    thisElement.set({ top: virtualTop });
+                                }else{
+                                    thisElement.set({ top: 0 });
+                                }
+
+                                if(virtualLeft > 0){
+                                    if(infiniteSpace || (virtualLeft + virtualWidth < pageWidth) ){
+                                        thisElement.set({ left: virtualLeft });
+                                    }else{
+                                        thisElement.set({ left: (pageWidth - virtualWidth) });
+                                    }
+                                }else{
+                                    thisElement.set({ left: 0 });
+                                }
+                                
+
+                            }else{
+
+
+
+                                if(virtualTop > 0){
+                                    for(var i = 0, l = elements.length; i < l; i++){
+                                        elements[i].set({
+                                            top:  elementsOffsetsY[i] + virtualTop
+                                        });
+                                    }
+                                    self.do('updateDimensions');
+                                }else{
+                                    myNode.style.top  = '0px';
+                                    for(var i = 0, l = elements.length; i < l; i++){
+                                        elements[i].set({
+                                            top:  elementsOffsetsY[i]
+                                        });
+                                    }
+                                }
+
+                                if(virtualLeft > 0){
+                                    if(infiniteSpace || (virtualLeft + virtualWidth < pageWidth) ){
+                                        for(var i = 0, l = elements.length; i < l; i++){
+                                            elements[i].set({
+                                                left:  elementsOffsetsX[i] + virtualLeft
+                                            });
+                                        }
+                                        self.do('updateDimensions');
+                                    }else{
+                                        myNode.style.left  = (pageWidth - virtualWidth) + 'px';
+                                        for(var i = 0, l = elements.length; i < l; i++){
+                                            elements[i].set({
+                                                left:  elementsOffsetsX[i] + (pageWidth - virtualWidth)
+                                            });
+                                        }
+                                    }
+                                }else{
+                                    myNode.style.left  = '0px';
+                                    for(var i = 0, l = elements.length; i < l; i++){
+                                        elements[i].set({
+                                            left:  elementsOffsetsX[i]
+                                        });
+                                    }
+                                }
+
+
+                                
+
+                            }
+
+                            startX = evt.pageX;
+                            startY = evt.pageY;
+                            
+
+                        }
+
+                       
+                        evt.stopPropagation();
+                        evt.preventDefault();
+
+                    };
+
+                elementNode.addEventListener('mousedown', onMouseDown, false);
+
+
+            }
+        }
+
+    }
+
+
+}});

+ 304 - 0
src/allplatforms/classes/Server.js

@@ -0,0 +1,304 @@
+SC.loadPackage({ 'Server': {
+
+
+    comment: 'I provide an interface to a SuperGlue server.',
+
+    properties: {
+        origin: { comment: 'I store the origin (protocol://hostname:port) of the server' }
+    },
+
+    methods: {
+
+        init: {
+            comment: 'I init a ServerInterface to a SuperGlue server. My argument is the origin',
+            code: function(origin){
+
+                this.set({ origin: origin });
+
+            }
+        },
+
+        upload: {
+            comment: 'I can perform an upload.'
+                    +'My argument is { path: aString, data: thePayload, onresponse: aFunction, onerror: aFunction, onprogress: aFunction }',
+            code: function(params){
+
+                var path = params.path.split(' ').join();
+                    xhr  = new XMLHttpRequest();
+
+                xhr.open('POST', this.get('origin') + path);
+
+                xhr.onload  = function(){
+                                    if(this.status === 200){
+                                        params.onresponse.call(this);
+                                    }else{
+                                        params.onerror.call(this);
+                                    }
+                                };
+                xhr.onerror = params.onerror;
+                xhr.onprogress = params.onprogress;
+
+                xhr.send(params.data);
+
+            }
+        },
+
+        uploadHTML: {
+            comment: 'I can perform an upload of an HTML page.'
+                    +'My argument is { path: aString, data: thePayload, onresponse: aFunction, onerror: aFunction, onprogress: aFunction }',
+            code: function(params){
+
+                var path = params.path.split(' ').join();
+                    xhr  = new XMLHttpRequest();
+
+                xhr.open('POST', this.get('origin') + path);
+                xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8')
+
+                xhr.onload  = function(){
+                                    if(this.status === 200){
+                                        params.onresponse.call(this);
+                                    }else{
+                                        params.onerror.call(this);
+                                    }
+                                };
+                xhr.onerror = params.onerror;
+                xhr.onprogress = params.onprogress;
+
+                xhr.send('data=' + encodeURIComponent(params.data));
+
+            }
+        },
+
+        cmdRequest: {
+            comment: 'I can perform a /cmd request for executing a linux shell command.'
+                    +'My argument is { cmd: aString, onresponse: aFunction, onerror: aFunction, onprogress: aFunction }',
+            code: function(params){
+
+                var xhr = new XMLHttpRequest();
+                xhr.open('POST', this.get('origin') + '/cmd');
+                xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8')
+
+                xhr.onload  = function(){
+                                    if(this.status === 200){
+                                        params.onresponse.call(this);
+                                    }else{
+                                        params.onerror.call(this);
+                                    }
+                                };
+                xhr.onerror = params.onerror;
+                xhr.onprogress = params.onprogress;
+
+                xhr.send('data=' + encodeURIComponent(params.cmd));
+
+            }
+        },
+
+
+        directoryListing: {
+            comment: 'I fetch a directory listing, { path: aString, onsuccess: aFunction, onerror: aFunction, onprogress: aFunction }',
+            code: function(params){
+
+                var path = params.path.split(' ').join('\\ ');
+                if(path === '/') path = '';
+
+                this.do('cmdRequest', {
+
+                    cmd: 'lss -la .' + path + '/',
+
+                    onerror: params.onerror,
+
+                    onprogress: params.onprogress,
+
+                    onresponse: function(){
+
+                        var directory = this.responseText.split('\n');
+                            directory.splice(-1, 1);
+                        
+
+                        for(var dirEntry, i = 0, l = directory.length; i < l; i++){
+
+                            dirEntry = directory[i].split('\t');
+
+                            directory[i] = {
+                                name:           dirEntry[0].slice(1),
+                                type:           dirEntry[1],
+                                size:           parseInt(dirEntry[2]),
+                                lastModified:   (new Date(dirEntry[3])),
+                                user:           dirEntry[4],
+                                group:          dirEntry[5],
+                                permissions:    parseInt(dirEntry[6]),
+                                isFile:         ("regular file" === dirEntry[1]),
+                                isDirectory:    ("directory"    === dirEntry[1])
+                            }
+
+                        }
+
+                        params.onsuccess.apply(directory);
+
+                    }
+
+                });
+
+            }
+        },
+
+        doesFileExist: {
+            comment: 'I check if a regular file exists, { path: aString, onsuccess: aFunction(aBoolean), onerror: aFunction, onprogress: aFunction }',
+            code: function(params){
+
+                var path = params.path.split(' ').join('\\ ');
+                
+                this.do('cmdRequest', {
+
+                    cmd: 'lss -la .' + path,
+
+                    onerror: params.onerror,
+
+                    onprogress: params.onprogress,
+
+                    onresponse: function(){
+
+                        params.onsuccess.call(params, this.responseText.split('\n')[0].length > 0);
+
+                    }
+
+                });
+
+            }
+        },
+
+        doesDirectoryExist: {
+            comment: 'I check if a directory exists, { path: aString, onsuccess: aFunction(aBoolean), onerror: aFunction, onprogress: aFunction }',
+            code: function(params){
+
+                var path = params.path.split(' ').join('\\ ');
+
+                path = path.split('/').slice(0, -1).join('/');
+
+                this.do('cmdRequest', {
+
+                    cmd: 'lss -la .' + path,
+
+                    onerror: params.onerror,
+
+                    onprogress: params.onprogress,
+
+                    onresponse: function(){
+
+                        params.onsuccess.call(params, this.responseText.indexOf(params.path) > -1);
+
+                    }
+
+                });
+
+            }
+        },
+
+
+        copyFile: {
+            comment: 'I copy a file, params = { sourcePath: aString, targetPath: aString, onsuccess: aFunction, onerror: aFunction, onprogress: aFunction }',
+            code: function(params){
+
+                var sourcePath = '.' + params.sourcePath.split(' ').join('\\ '),
+                    targetPath = '.' + params.targetPath.split(' ').join();
+
+                this.do('cmdRequest', {
+
+                    cmd: 'cp ' + sourcePath + ' ' + targetPath,
+
+                    onerror:    params.onerror,
+                    onprogress: params.onprogress,
+                    onresponse: params.onsuccess
+
+                });
+
+            }
+        },
+
+
+        moveFile: {
+            comment: 'I move a file, params = { sourcePath: aString, targetPath: aString, onsuccess: aFunction, onerror: aFunction, onprogress: aFunction }',
+            code: function(params){
+
+                var sourcePath = '.' + params.sourcePath.split(' ').join('\\ '),
+                    targetPath = '.' + params.targetPath.split(' ').join();
+
+                this.do('cmdRequest', {
+
+                    cmd: 'mv ' + sourcePath + ' ' + targetPath,
+
+                    onerror:    params.onerror,
+                    onprogress: params.onprogress,
+                    onresponse: params.onsuccess
+
+                });
+
+            }
+        },
+
+
+        removeFile: {
+            comment: 'I remove a file, params = { path: aString, onsuccess: aFunction, onerror: aFunction, onprogress: aFunction }',
+            code: function(params){
+
+                var path = '.' + params.path.split(' ').join('\\ ');
+
+                this.do('cmdRequest', {
+
+                    cmd: 'rm ' + path,
+
+                    onerror:    params.onerror,
+                    onprogress: params.onprogress,
+                    onresponse: params.onsuccess
+
+                });
+
+            }
+        },
+
+        makeDirectory: {
+            comment: 'I make a directory, params = { path: aString, onsuccess: aFunction, onerror: aFunction, onprogress: aFunction }',
+            code: function(params){
+
+                var path = '.' + params.path.split(' ').join();
+
+                this.do('cmdRequest', {
+
+                    cmd: 'mkdir ' + path,
+
+                    onerror:    params.onerror,
+                    onprogress: params.onprogress,
+                    onresponse: params.onsuccess
+
+                });
+
+            }
+        },
+
+        removeDirectory: {
+            comment: 'I remove a directory, params = { path: aString, onsuccess: aFunction, onerror: aFunction, onprogress: aFunction }',
+            code: function(params){
+
+                var path = '.' + params.path.split(' ').join('\\ ');
+
+                this.do('cmdRequest', {
+
+                    cmd: 'rm -r ' + path,
+
+                    onerror:    params.onerror,
+                    onprogress: params.onprogress,
+                    onresponse: params.onsuccess
+
+                });
+
+            }
+        }
+
+
+
+
+
+
+    }
+
+}});

+ 172 - 0
src/allplatforms/classes/SliderWidget.js

@@ -0,0 +1,172 @@
+SC.loadPackage({ 'SliderWidget': {
+
+    comment: 'I am a trait class for Widget classes and provide the functionality for a slider.',
+
+    traits: ['Widget'],
+
+    
+    properties: {
+
+        widgetPanel:    { comment: 'I store the DOMElement containing the panel of controls of the widget.' },
+
+        isWidgetActive: { comment: 'Wether the widget is active.',
+                          transform: function(aBoolean){
+                            
+                                if(aBoolean){
+
+                                    if(this.get('widgetPanel').parentNode !== this.get('widgetMenu')){
+                                        this.get('widgetMenu').appendChild(this.get('widgetPanel'));
+                                    }
+                                    this.get('widgetMenu').classList.add('active');
+
+                                    // update Value
+
+                                }else{
+
+                                    if(this.get('widgetPanel').parentNode === this.get('widgetMenu')){
+                                        this.get('widgetMenu').removeChild(this.get('widgetPanel'));
+                                    }
+                                    this.get('widgetMenu').classList.remove('active');
+                                
+                                }
+                                return aBoolean
+                          }
+                        }
+
+    },
+
+    methods: {
+
+        initSliderWidget: { 
+            comment:    'I init the widget as a SliderWidget.',
+            code:       function(sliderConfig){
+
+                var widgetPanel = '<div class="sg-editing-widget-slider-panel">'
+                                        +'<div class="sg-editing-widget-panel">'
+                                            +'<div class="sg-widget-triangle-up"></div>'
+                                            +'<div class="sg-editing-widget-slider-range">'
+                                                +'<div class="sg-editing-widget-slider-handle" style="top: 0px;"></div>'
+                                            +'</div>'
+                                        +'</div>'
+                                    +'</div>',
+
+
+                    self        = this,
+                    widgetPanel = (new DOMParser()).parseFromString(widgetPanel, 'text/html').body.firstChild,
+                    handle      = widgetPanel.querySelector('.sg-editing-widget-slider-handle');
+                
+                widgetPanel.querySelector('.sg-editing-widget-panel').addEventListener('mouseup', function(evt){
+                    sliderConfig.theSelection.set({ activeWidget: self });
+                    evt.stopPropagation();
+                }, false);
+
+                
+
+                var startY,
+                    maxY = 110,
+                    clickOffset,
+                    currentY,
+
+                    onMouseDownOnRange = function(evt){
+
+                        sliderConfig.theSelection.set({ activeWidget: self });
+                        
+
+                         
+                        var currentElement = evt.currentTarget,
+                            yPosition = 0,
+                            newY;
+                        
+                        if(navigator.userAgent.indexOf('Firefox') > 0){
+                            while(currentElement){
+                                console.log('yes')
+                                yPosition += (currentElement.offsetTop + currentElement.clientTop);
+                                currentElement = currentElement.offsetParent;
+                            }
+                            yPosition -= document.documentElement.scrollTop;
+                        }else{
+                            while(currentElement){
+                                yPosition += (currentElement.offsetTop - currentElement.scrollTop + currentElement.clientTop);
+                                currentElement = currentElement.offsetParent;
+                            }
+                        }
+
+                        newY = evt.clientY - yPosition;
+
+                        if(newY < maxY){
+                            handle.style.top = newY + 'px';
+                            sliderConfig.setCallback.call(self, newY / maxY);
+                        }else{
+                            handle.style.top = maxY + 'px';
+                            sliderConfig.setCallback.call(self, 1);
+                        }
+
+
+                        evt.stopPropagation();
+                        evt.preventDefault();
+
+                    },
+
+                    onMouseDownOnHandle = function(evt){
+
+                        sliderConfig.theSelection.set({ activeWidget: self });
+                        
+
+                        currentY = parseInt(handle.style.top);
+                        startY   = evt.pageY - currentY;
+                        
+                        SuperGlue.get('document').set({ interactionInProgress: true });
+                        document.addEventListener('mousemove', onMouseMove, true);
+                        document.addEventListener('mouseup', onMouseUp, true);
+                        evt.stopPropagation();
+                        evt.preventDefault();
+
+                    },
+
+                    onMouseMove = function(evt){
+                        
+                        currentY = evt.pageY - startY;
+
+                        if(currentY < 0){
+                            handle.style.top = '0px';
+                            sliderConfig.setCallback.call(self, 0);
+                        }else if(currentY >= maxY){
+                            handle.style.top = maxY + 'px';
+                            sliderConfig.setCallback.call(self, 1);
+                        }else{
+                            handle.style.top = currentY + 'px';
+                            sliderConfig.setCallback.call(self, currentY / maxY);
+                        }
+
+                        evt.stopPropagation();
+                        evt.preventDefault();
+                    },
+
+                    onMouseUp = function(evt){
+                        
+                        SuperGlue.get('document').set({ interactionInProgress: false });
+                        document.removeEventListener('mousemove', onMouseMove, true);
+                        document.removeEventListener('mouseup', onMouseUp, true);
+                        evt.stopPropagation();
+                        evt.preventDefault();
+                    };
+
+                widgetPanel.querySelector('.sg-editing-widget-slider-handle').addEventListener('mousedown', onMouseDownOnHandle, false);
+
+                widgetPanel.querySelector('.sg-editing-widget-slider-range').addEventListener('mousedown', onMouseDownOnRange, false);
+
+                handle.style.top = sliderConfig.startValue * maxY + 'px';
+                
+                this.set({ 
+                    widgetPanel: widgetPanel
+                });
+
+            }
+
+        }
+
+
+    }
+
+
+}});

+ 186 - 0
src/allplatforms/classes/SuperGlue.js

@@ -0,0 +1,186 @@
+SC.loadPackage({ 'SuperGlue': {
+
+
+    comment: 'Hello friend, I am SuperGlue!\nMy single instance is the central object of the system. It provides the start routine with method SuperGlue>>init, and stores itsself in the global variable window.SuperGlue',
+
+    sharedProperties: {
+
+        version: {  comment:   'SuperGlue\'s current version is...', 
+                    initValue: '1.0' 
+                 }
+
+    },
+
+    properties: {
+
+        document:         { comment: 'This is the current document.' },
+        
+        selection:        { comment: 'The selection is the focus of the editor, holding on or more elements to edit.' },
+        clipboard:        { comment: 'This is a proxy to the operating system\'s clipboard.' },
+        history:          { comment: 'The editing history is a stack object with undo/redo-methods.' },
+        
+        windowManager:    { comment: 'I have a windowManager which holds the 2nd-level editing interface.' },
+        keyboard:         { comment: 'This is a controller object processing keyboard commands.' },
+        server:           { comment: 'I keep an interface to the server under the current domain (if available).' },
+        fileManager:      { comment: 'I provide a FileManager for the user\'s file system on the server (if available).' },
+        
+
+        askBeforeUnload:  { comment: 'Shall I ask the user before reloading the page?',
+                            transform: function(val){
+                                return val
+                                    ? (window.onbeforeunload = function(){ 
+                                        return 'Do you want to cancel your work and don\'t save it?' 
+                                      })
+                                    : (window.onbeforeunload = void 0);
+                                }
+                            }
+
+
+    },
+
+    methods: {
+
+        init: {
+            comment:  'I am SuperGlue\'s start routine, yay!',
+            code:     function(){
+
+                // Initialization of the whole system is "mission critical", 
+                // so any failure should be catched and reported to the user.
+                try {
+                    
+                    // Set SmallClasses's error handling during init
+                    SC.setHandlerForMessageNotUnderstood(function(selector, errorMsg, context){
+                        console.log('Error with selector: ', selector, ' in context: ', context);
+                        throw new Error(errorMsg);
+                    });
+
+                    // Explicit failures
+                    var metaData = document.querySelector('meta[name=generator]');
+                    if( metaData.getAttribute('content') !== 'SuperGlue' ){
+                        throw new Error('This is not a SuperGlue page, you can\'t change it.')
+                    }
+                    if( this.do('compareVersions', {
+                            pageVersion:   metaData.getAttribute('data-superglue-version'), 
+                            pluginVersion: this.class.get('version')
+                        })
+                    ){
+                        throw new Error(
+                              'This page needs version '
+                            + metaData.getAttribute('data-superglue-version')
+                            + '. Please update your SuperGlue browser add-on.'
+                        )
+                    }
+
+
+                    // Make myself a global object (the only one, thou shall not have others beside me)
+                    window.SuperGlue = this;
+
+
+                    // Initialize all main components of the system                    
+
+                    this.set({ document: SC.init('Document') });
+                    this.set({ 
+                        selection:        SC.init('Selection'),
+                        clipboard:        SC.init('Clipboard'),
+                        history:          SC.init('History'),
+                        windowManager:    SC.init('WindowManager'),
+                        keyboard:         SC.init('Keyboard'),
+                        server:           SC.init('Server', window.document.location.origin),
+                        fileManager:      SC.init('FileManager')
+                    });
+
+                    this.get('document').do('setUpWorkspace');
+                    
+
+                    // Finish initialization
+
+                    this.set({'askBeforeUnload' : true});
+                    
+                    var editingMarker = document.createElement('meta');
+                    editingMarker.setAttribute('name', 'superglue-mode');
+                    editingMarker.setAttribute('content', 'editing');
+                    editingMarker.setAttribute('data-superglue', 'editing-interface');
+                    document.getElementsByTagName('head')[0].appendChild(editingMarker);
+
+
+                    // Reset SmallClasses's error handling to normal
+                    SC.setHandlerForMessageNotUnderstood();
+                    
+                } catch(error) {
+                    // Catch any initialization error
+                    alert('Something went wrong starting SuperGlue\'s editing tool.\n\n' + error.message);
+                    console.log('Failed to initialize SuperGlue:\n', error);
+                    return;
+                }
+
+
+            }
+        },
+
+
+        savePage: {
+
+            comment: 'I save the current page to a SuperGlue server. My parameters are { path: aString, <optional>remoteOrigin: aString }.',
+            code:    function(saveOptions){
+
+                var thisPage = SC.init('Compiler').get('pageAsHTML5'),
+                    server   = saveOptions.remoteOrigin
+                                ? SC.init('Server', saveOptions.remoteOrigin)
+                                : this.get('server');
+
+                
+                server.do('uploadHTML', {
+                    path:   saveOptions.path,
+                    data:   thisPage,
+                    onerror:    function(){
+                                    console.log(this);
+                                    alert('Critical error: The page could not be saved.\nSee console for more details.');
+                                },
+                    onprogress: function(){
+
+                                },
+                    onresponse: function(){
+                                    alert('Page successfully saved.\n\n' + server.get('origin') + '/' + saveOptions.path);
+                                }
+                })
+
+
+            }
+        },
+
+
+
+        compareVersions: {
+            comment:  'I check for version compatability of page and plugin. Return is true for out-of-date!',
+            code:     function (arg) {
+                
+                var v1 = arg.pageVersion,
+                    v2 = arg.pluginVersion,
+                    v1parts = v1.split('.'),
+                    v2parts = v2.split('.');
+                for (var i = 0; i < v1parts.length; ++i) {
+                    if (v2parts.length == i) {
+                        return true;
+                    }
+                    if (v1parts[i] == v2parts[i]) {
+                        continue;
+                    }
+                    else if (v1parts[i] > v2parts[i]) {
+                        return true;
+                    }
+                    else {
+                        return false;
+                    }
+                }
+                if (v1parts.length != v2parts.length) {
+                    return false;
+                }
+                return false;
+            }
+        }
+
+
+
+    }
+
+}});

+ 440 - 0
src/allplatforms/classes/TextEditor.js

@@ -0,0 +1,440 @@
+SC.loadPackage({ 'TextEditor': {
+
+    comment: 'I am the TextEditor for TextElements.',
+
+    sharedProperties: {
+
+        editGroupFontFamily:    { initValue: '<a class="btn dropdown-toggle" title="Font"><i class="icon-font"></i><b class="caret"></b></a><ul class="dropdown-menu"></ul>' },
+        editGroupFontSize:      { initValue: '<a class="btn dropdown-toggle" title="Font Size"><i class="icon-text-height"></i>&nbsp;<b class="caret"></b></a><ul class="dropdown-menu small"></ul>' },
+        editGroupFontStyle:     { initValue: '<a class="btn" data-wysihtml5-command="bold" title="Bold"><i class="icon-bold"></i></a><a class="btn" data-wysihtml5-command="italic" title="Italic"><i class="icon-italic"></i></a><a class="btn" data-wysihtml5-command="underline" title="Underline"><i class="icon-underline"></i></a>' },
+        editGroupFontColor:     { initValue: '<a class="btn dropdown-toggle right" title="Font Color"><i class="icon-color">&nbsp;<b class="caretRight"></i></a><div class="colorpicker2"></div>' },
+        editGroupLists:         { initValue: '<a class="btn" data-wysihtml5-command="insertUnorderedList" title="Bullet list"><i class="icon-list-ul"></i></a><a class="btn" data-wysihtml5-command="insertOrderedList" title="Number list"><i class="icon-list-ol"></i></a>' },
+        editGroupIndention:     { initValue: '<a class="btn" data-wysihtml5-command="outdent" title="Reduce indent"><i class="icon-indent-right"></i></a><a class="btn" data-wysihtml5-command="indent" title="Indent"><i class="icon-indent-left"></i></a>' },
+        editGroupAlignment:     { initValue: '<a class="btn" data-wysihtml5-command="alignLeftStyle" title="Align Left"><i class="icon-align-left"></i></a><a class="btn" data-wysihtml5-command="alignCenterStyle" title="Center"><i class="icon-align-center"></i></a><a class="btn" data-wysihtml5-command="alignRightStyle" title="Align Right"><i class="icon-align-right"></i></a><a class="btn" data-wysihtml5-command="alignJustifyStyle" title="Justify"><i class="icon-align-justify"></i></a>' },
+        editGroupHyperlink:     { initValue: '<a class="btn dropdown-toggle hyperlink" title="Hyperlink" data-wysihtml5-command="createLink"><i class="icon-link"></i>&nbsp;<b class="caret"></b></a><div class="dropdown-menu input-append" data-wysihtml5-dialog="createLink" style="display: none;"><label>Link:<input data-wysihtml5-dialog-field="href" value="http://" class="text"></label><a data-wysihtml5-dialog-action="save">OK</a> <a data-wysihtml5-dialog-action="cancel">Cancel</a> <a data-wysihtml5-command="removeLink">Remove</a></div>' },
+        activeEditGroups:       { initValue: ['editGroupIndention', 'editGroupAlignment', 'editGroupFontSize', 'editGroupFontFamily', 'editGroupFontColor', 'editGroupHyperlink', 'editGroupFontStyle', 'editGroupLists'] },
+        activeFonts:            { initValue: ['Serif', 'Sans', 'Arial', 'Arial Black', 'Courier', 'Courier New', 'Comic Sans MS','Dosis', 'Helvetica', 'Impact', 'Lucida Grande', 'Lucida Sans', 'Montserrat', 'Tahoma', 'Times', 'Times New Roman', 'TitilliumWeb', 'Verdana'] },
+        activeFontSizes:        { initValue: ['8px', '9px', '10px', '11px', '12px', '13px', '14px', '15px', '16px', '17px', '18px', '20px', '22px', '26px', '28px', '30px', '32px', '34px', '36px', '38px', '40px', '46px', '50px', '60px', '70px'] }
+
+
+    },
+
+    properties: {
+
+        originalElementNode:    { comment: 'I store the original TextElement\'s DOM node.' },
+        originalSelection:      { comment: 'I store the selection to restore it on unloading of the editor.' },
+        textEditor:             { comment: 'I store the TextEditor\'s DOM node.' },
+        textEditorContainer:    { comment: 'I store the textEditorContainer\'s DOM node.' }
+
+    },
+
+    methods: {
+
+    	init: { 
+    		comment: 	'method comment',
+    		code: 		function(aTextElement){
+
+                var self = this,
+                    originalElementNode = aTextElement.get('node'),
+                    textEditor          = document.createElement('textarea'),
+                    textEditorContainer = document.createElement('div');
+
+                this.set({
+                    originalElementNode:    originalElementNode,
+                    originalSelection:      SuperGlue.get('selection').get('elements'),
+                    textEditor:             textEditor,
+                    textEditorContainer:    textEditorContainer
+                });
+
+                SuperGlue.get('selection').do('clearAll');
+
+                // Prepare textEditor's div
+                /*
+                textEditor.setAttribute('id', 'sg-editing-textEditor');
+                textEditor.style.width   = originalElementNode.style.width;
+                textEditor.style.height  = originalElementNode.style.height;
+                textEditor.style.padding = originalElementNode.style.padding;
+                textEditor.style.border  = originalElementNode.style.border;
+                textEditor.style.borderRadius  = originalElementNode.style.borderRadius;
+                */
+
+                // Add textarea
+
+                textEditor.setAttribute('id', 'currentEditor');
+                textEditor.style.borderWidth        = originalElementNode.style.borderWidth;
+                textEditor.style.borderColor        = originalElementNode.style.borderColor;
+                textEditor.style.borderRadius       = originalElementNode.style.borderRadius;
+                textEditor.style.backgroundColor    = originalElementNode.style.backgroundColor;
+                textEditor.style.backgroundRepeat   = originalElementNode.style.backgroundRepeat;
+                textEditor.style.backgroundImage    = originalElementNode.style.backgroundImage;
+                textEditor.style.padding            = originalElementNode.style.padding;
+                textEditor.style.boxSizing          = 'border-box';
+
+                textEditor.style.position   = 'absolute';
+                textEditor.style.width      = this.get('originalElementNode').offsetWidth + 'px';
+                textEditor.style.height     = this.get('originalElementNode').offsetHeight + 'px';
+
+                textEditor.style.outline   = '1px dashed rgb(255, 41, 61)';
+
+                this.do('updateEditorDimensions');
+                
+                /*
+                textEditor.style.top = this.get('originalElementNode').clientTop - this.get('originalElementNode').scrollTop + 'px';
+                textEditor.style.left = this.get('originalElementNode').clientLeft - this.get('originalElementNode').scrollLeft + 'px';
+                textEditor.style.width = this.get('originalElementNode').offsetWidth + 'px';
+                textEditor.style.height = this.get('originalElementNode').offsetHeight + 'px';
+                */
+
+                this.get('textEditorContainer').appendChild(textEditor);
+
+                window.addEventListener('resize', function(){ 
+                    self.do('updateEditorDimensions', textEditor) 
+                }, false);
+
+                // Prepare container
+                
+                textEditorContainer.setAttribute('id', 'sg-editing-textEditor-container');
+                this.do('updateContainerDimensions', textEditorContainer);
+                window.addEventListener('resize', function(){ 
+                    self.do('updateContainerDimensions', textEditorContainer) 
+                }, false);
+
+
+
+                textEditor.innerHTML = originalElementNode.innerHTML;
+                originalElementNode.style.display = 'none';
+
+                textEditorContainer.appendChild(textEditor);
+
+
+                // Add eventListener on the container
+
+                textEditorContainer.addEventListener('mouseover', function(evt){
+                    evt.stopPropagation();
+                }, false);
+
+                textEditorContainer.addEventListener('mouseout', function(evt){
+                    evt.stopPropagation();
+                }, false);
+
+
+                textEditorContainer.addEventListener('mouseup', function(evt){
+                    evt.stopPropagation();
+                }, false);
+
+                textEditorContainer.addEventListener('mousedown', function(evt){
+
+                    self.do('closeTextEditor');
+
+                    evt.stopPropagation();
+
+                }, false);
+
+
+                document.body.insertBefore(
+                    textEditorContainer, 
+                    SuperGlue.get('document').get('editingContainer').nextElementSibling
+                );
+
+                // Append text editing toolbar
+
+                var textShapeToolbar = document.createElement('div');
+                    textShapeToolbar.setAttribute('id', 'textShapeToolbar');
+                    textShapeToolbar.setAttribute('data-role', 'editor-toolbar');
+                    textShapeToolbar.setAttribute('data-target', '#currentEditor');
+                    textShapeToolbar.style.display = 'block';
+
+                    textShapeToolbar.style.top = this.get('textEditor').offsetTop - 85 + 'px';
+                    textShapeToolbar.style.left = this.get('textEditor').offsetLeft + this.get('textEditor').offsetWidth - 300 + 'px';
+
+                var editGroups = this.class.get('activeEditGroups');
+
+                for ( i=0; i < editGroups.length; i++ ) {
+                    var htmlString = this.class.get(editGroups[i]);
+                    var currentGroup = document.createElement('div');
+                        currentGroup.setAttribute('class', 'btn-group');
+                        currentGroup.innerHTML = htmlString;
+                        textShapeToolbar.appendChild(currentGroup);
+                }
+
+                textShapeToolbar.addEventListener('mousedown', function(evt) {
+                    evt.stopPropagation();
+                });
+
+                this.get('textEditorContainer').appendChild(textShapeToolbar);
+
+                this.do('initToolbarBindings');
+
+                textEditor.style.display = 'none';
+
+                // Initialize Editor
+
+                window.editor = new wysihtml5.Editor("currentEditor", { // id of textarea element
+                  toolbar:      "textShapeToolbar", // id of toolbar element
+                  style:        false, 
+                  useLineBreaks: false,
+                  parserRules:  wysihtml5ParserRules, // defined in parser rules set
+                  cleanUp:      true, 
+                  stylesheets:  ["../resources/default/css/normalize.css", "../resources/default/css/style.css"]
+                }).on('load', function() {
+                    
+                    document.querySelector('.wysihtml5-sandbox').classList.add('shapeContent');
+                    editor.focus();
+                    
+                }).on('blur', function() {
+                    
+                    document.querySelector('.wysihtml5-sandbox').style.display = 'block';
+                    self.get('originalElementNode').innerHTML = self.get('textEditor').value;
+                    self.get('textEditor').style.display = 'none';
+
+                }).on('focus', function() {
+                    
+                    self.get('textEditor').style.display = 'none';
+                    var sandbox = document.querySelector('.wysihtml5-sandbox');
+                        sandbox.style.borderWidth = self.get('textEditor').style.borderWidth;
+                        sandbox.style.borderColor = self.get('textEditor').style.borderColor;
+                        sandbox.style.borderRadius = self.get('textEditor').style.borderRadius;
+                        sandbox.style.backgroundColor = self.get('textEditor').style.backgroundColor;
+                        sandbox.style.backgroundRepeat = self.get('textEditor').style.backgroundRepeat;
+                        sandbox.style.backgroundImage = self.get('textEditor').style.backgroundImage;
+                        sandbox.style.padding = self.get('textEditor').style.padding;
+                        sandbox.style.boxSizing = self.get('textEditor').style.boxSizing;
+                        sandbox.style.outline = self.get('textEditor').style.outline;
+
+                        sandbox.style.position = 'absolute';
+                        //this.do('updateEditorDimensions', sandbox);
+                        sandbox.style.top = self.get('textEditor').style.top;
+                        sandbox.style.left = self.get('textEditor').style.left;
+                        sandbox.style.width = self.get('textEditor').style.width;
+                        sandbox.style.height = self.get('textEditor').style.height;
+                        
+                        sandbox.style.display = 'block';
+                    
+                });
+                /*                        
+                self.get('textEditor').addEventListener('click', function(evt){
+
+                    if (editor) {
+                        window.editor.focus(); 
+                    }
+                    
+                    evt.stopPropagation();
+                    return;
+                });
+                */
+
+                var self = this;
+                var tmpTextColor;
+                
+                
+                document.querySelector('.wysihtml5-sandbox').contentWindow.document.body.addEventListener('selectstart', function () {
+                    
+                    var fired = false;
+
+                    document.querySelector('.wysihtml5-sandbox').contentWindow.addEventListener('mouseup', function(evt) {
+                        if (!fired) {
+                            if (this.getSelection().type != 'Range') {
+                                                                
+                                //$('#textShapeToolbar .colorpicker2').removeClass('active');
+                                tmpTextColor = "rgb(0,0,0)";
+
+                            } else {
+                                
+                                var currentFontColor = undefined;
+
+                                var sel = editor.composer.selection.getSelection();
+                                if (sel.rangeCount > 0) {
+                                    var range = sel.getRangeAt(0);
+                                    var parentElement = range.commonAncestorContainer;
+                                    if (parentElement.nodeType == 3) {
+                                        parentElement = parentElement.parentNode;
+                                    }
+                                    if (parentElement.style.color.length > 0) {
+                                        currentFontColor = parentElement.style.color;
+                                    }
+                                }
+
+                                // TODO: INIT COLORPICKER
+                                //self.do('initPicker', { initColor: currentFontColor });
+                                
+                            }
+
+                            fired = true;
+                        }
+                        
+                    });
+                    
+
+                });
+
+                
+
+    		}
+    	},
+
+        closeTextEditor: {
+            comment: '',
+            code: function(){
+
+                // close routine
+
+                this.get('originalElementNode').innerHTML = this.get('textEditor').value;
+                this.get('originalElementNode').style.display = '';
+
+                document.body.removeChild(this.get('textEditorContainer'));
+
+                this.get('originalSelection').forEach(function(element){
+                    SuperGlue.get('selection').do('addElement', element)
+                });
+
+
+            }
+        },
+
+        updateEditorDimensions: {
+            comment: '',
+            code: function(){
+
+                var currentElement = this.get('originalElementNode'),
+                    top, left;
+                      
+                left = (- parseInt(currentElement.style.borderWidth));
+                left = top = isNaN(left) ? 0 : left
+
+                while(currentElement){
+                    top  += (currentElement.offsetTop  + currentElement.clientTop );
+                    left += (currentElement.offsetLeft + currentElement.clientLeft);
+                    currentElement = currentElement.offsetParent;
+                }
+
+                this.get('textEditor').style.top  = top  + 'px';
+                this.get('textEditor').style.left = left + 'px';
+
+            }
+        },
+
+        updateContainerDimensions: {
+            comment: '',
+            code: function(textEditorContainer){
+
+                var maxWidth  = SuperGlue.get('document').do('getMinWidth'),
+                    maxHeight = SuperGlue.get('document').do('getMinHeight');
+
+                maxWidth  = maxWidth  > (window.innerWidth  - 20) ? maxWidth  : (window.innerWidth  - 20);
+                maxHeight = maxHeight > (window.innerHeight - 20) ? maxHeight : (window.innerHeight - 20);
+
+                textEditorContainer.style.width  = maxWidth  + 'px';
+                textEditorContainer.style.height = maxHeight + 'px';
+
+            }
+        },
+
+        initToolbarBindings: {
+            comment:  'I initialize event bindings for the textShapeToolbar before wysihtml5 is executed.',
+            params:   {},
+            code:     function() {
+
+                var fonts = this.class.get('activeFonts');
+                var fontTarget = document.querySelector('[title="Font"]').parentNode.querySelector('.dropdown-menu');
+                
+                for (var i=0; i<fonts.length; i++) {
+                    var fontName = fonts[i];
+                    var fontBtn = document.createElement('li');
+                        fontBtn.innerHTML = '<a data-wysihtml5-command="fontFamilyStyle" data-wysihtml5-command-value="' + fontName +'">'+ fontName + '</a>';
+                    fontTarget.appendChild(fontBtn);
+                }
+
+                var fontSizes = this.class.get('activeFontSizes'),
+                    fontSizeTarget = document.querySelector('[title="Font Size"]').parentNode.querySelector('.dropdown-menu');
+
+                for (var s=0; s<fontSizes.length; s++) {
+                    var fontSize = fontSizes[s];
+                    var fontSizeBtn = document.createElement('li');
+                        fontSizeBtn.innerHTML = '<a data-wysihtml5-command="fontSizeStyle" data-wysihtml5-command-value="' + fontSize +'">'+ fontSize + '</a>';
+                    fontSizeTarget.appendChild(fontSizeBtn);
+                }
+
+                
+                document.querySelector('.dropdown-menu input').addEventListener('click', function() {return false;});
+                /*
+                document.querySelector('.dropdown-menu input').addEventListener('change', function() {
+                    this.parentNode.parentNode.querySelector('.dropdown-toggle').dropdown('toggle');
+                });
+                */
+                   
+                document.querySelector('.dropdown-menu input').addEventListener('keydown', function (evt) {
+                    if (evt.keyCode == 27) {
+                        this.value='';
+                    }
+                });
+                
+
+                var dropdownMenus = document.querySelectorAll('.dropdown-toggle');
+                
+                for (var d=0; d<dropdownMenus.length; d++) {
+                    dropdownMenus[d].addEventListener('click', function() {
+
+                        /*
+                        var dropdownClass;
+                        if ( this.nextElementSibling && this.nextElementSibling.classList.contains('colorpicker2') ) {
+                            dropdownClass = 'colorpicker2';
+                        } else {
+                            dropdownClass = 'dropdown-menu';
+                        }
+                        */
+                        
+                        var dropdownClass = 'dropdown-menu'
+
+                        if ( this.classList.contains('open') ) {
+                            this.nextElementSibling.classList.remove('active');
+                            this.classList.remove('open');
+                        } else {
+                            var dropdownMenuContainers = document.querySelectorAll('.dropdown-menu');
+
+                            for (var i=0; i<dropdownMenuContainers.length; i++) {
+                                dropdownMenuContainers[i].classList.remove('active')
+                            }
+                            
+                            this.nextElementSibling.classList.add('active');
+                            
+                            for (var d=0; d<dropdownMenus.length; d++) {
+                                dropdownMenus[d].classList.remove('open');
+                            }
+
+                            this.classList.add('open');
+                        }
+
+                    });
+                }
+
+                var buttons = document.querySelectorAll('.dropdown-menu li, .dropdown-menu button, .dropdown-menu a[data-wysihtml5-dialog-action]');
+
+                for (var b=0; b<buttons.length; b++) {
+                    buttons[b].addEventListener('click', function() {
+                        this.parentNode.classList.remove('active');
+                        for (var d=0; d<dropdownMenus.length; d++) {
+                            dropdownMenus[d].classList.remove('open');
+                        }
+                    });
+                }
+                
+            }
+        },
+
+        initPicker: {
+            comment: 'I init my colorpicker. Params: initColor',
+            code: function(){
+
+                // TODO: INIT COLORPICKER HERE
+                // with arg.initColor
+
+
+            }
+        },
+
+
+
+    }
+
+
+}});

+ 49 - 0
src/allplatforms/classes/TextElement.js

@@ -0,0 +1,49 @@
+SC.loadPackage({ 'TextElement': {
+
+    comment: 'I define a text element.',
+
+    traits: [ 'Element' ],
+    
+    
+    sharedProperties: {
+        protoHTML:          { initValue: '<div class="sg-element" data-superglue-type="TextElement" style="left: 0px; top: 0px; width: 0px; height: 0px;">'
+                                        +'\t<p style="font-family: Helvetica; font-size: 18px; color: rgb(255, 255, 255);">Hello friend, I am SuperGlue.</p>'
+                                        +'</div>' },
+        applicableWidgets:  { initValue: [ 'WidgetBackgroundColor', 'WidgetBorderColor', 'WidgetBorder', 'WidgetBorderRadius', 'WidgetPadding', 'WidgetOpacity' ] },
+        creationMenuItem:   { initValue: '<div class="sg-editing-creation-menu-container"><button id="sg-editing-creation-menu-textElement" class="sg-editing-creation-menu-button" data-tooltip="Text"></button></div>' }
+    },
+
+    properties: {
+
+    },
+
+    methods: {
+
+        init: { 
+            comment:    'I initalize a new TextElement for the DOM node given to me as argument.',
+            code:       function(aNode){
+
+                this.delegate('Element', 'init', aNode);
+
+                // Custom initialisation here
+
+                
+
+            }
+        },
+
+        activateTextEditor: { 
+            comment:    'I start the text editor on my element.',
+            code:       function(){
+
+
+                SC.init('TextEditor', this);
+                
+
+            }
+        }
+
+    }
+
+
+}});

+ 110 - 0
src/allplatforms/classes/Widget.js

@@ -0,0 +1,110 @@
+SC.loadPackage({ 'Widget': {
+
+    comment: 'I am the abstract base class of all widgets. Subclasses must include me as a trait, and call the init method of this class. Every Subclass must have a shared property called widgetMenu, which contains as a string the html of its widgetMenu.',
+
+
+    properties: {
+
+        selection:      { comment: 'I store a reference to the selection of the editing tool.' },
+
+        widgetMenu:     { comment: 'I store a DOMElement containing my .widgetMenuContainer node.' },
+        widgetButton:   { comment: 'I store a DOMElement containing my .widgetButton node.' },
+
+        isActionButton: { comment: 'An action button does not stay active after being clicked. I should be set during mySubclass>>init' },
+
+        isWidgetActive: { comment: 'Wether the widget is active. Transform function can be overriden by my subclasses to show e.g. a panel.',
+                          transform: function(aBoolean){
+                                if(aBoolean){
+                                    this.get('widgetMenu').classList.add('active');
+                                }else{
+                                    this.get('widgetMenu').classList.remove('active');
+                                }
+                                return aBoolean
+                          }
+                        }
+
+    },
+
+    methods: {
+
+    	init: { 
+    		comment: 	'I init a widget, therefor all my subclasses must call me via this.delegate(\'Widget\', \'init\', theSelection).',
+    		code: 		function(theSelection){
+
+                var self = this,
+                    widgetMenu  = (new DOMParser()).parseFromString(this.class.get('widgetMenu'), 'text/html').body.firstChild;
+
+
+                widgetMenu.addEventListener('mouseenter', function(evt){
+
+                    if(theSelection.get('activeWidget') === null){
+                        self.set({ isWidgetActive: true });
+                    }
+
+                    evt.stopPropagation();
+
+                }, false);
+
+                widgetMenu.addEventListener('mouseleave', function(evt){
+
+                    if(theSelection.get('activeWidget') !== self){
+                        self.set({ isWidgetActive: false });
+                    }
+
+                    evt.stopPropagation();
+
+                }, false);
+                
+
+                widgetMenu.addEventListener('mouseup', function(evt){
+
+                    var activeWidget = theSelection.get('activeWidget');
+
+                    if(self.get('isActionButton')){
+
+                        theSelection.set({ activeWidget:   null  });
+                        if(activeWidget){ 
+                            activeWidget.set({ isWidgetActive: false }) 
+                        }
+                        self.set({ isWidgetActive: false });
+
+                    }else{
+                        
+                        if(activeWidget === self){
+                            theSelection.set({ activeWidget:   null });
+                        }else if(activeWidget === null){
+                            theSelection.set({ activeWidget: self });
+                        }else{
+                            theSelection.set({ activeWidget: self });
+                            activeWidget.set({ isWidgetActive: false });
+                                    self.set({ isWidgetActive: true  });
+                        }
+
+                    }
+
+                    
+                }, false);
+
+                
+
+                widgetMenu.addEventListener('mousedown', function(evt){
+                    evt.stopPropagation();
+                }, false);
+
+
+                this.set({
+                    selection:      theSelection,
+                    widgetMenu:     widgetMenu,
+                    widgetButton:   widgetMenu.firstElementChild,
+                    isActionButton: false
+                });
+
+    		}
+
+    	}
+
+
+    }
+
+
+}});

+ 61 - 0
src/allplatforms/classes/WidgetBackgroundColor.js

@@ -0,0 +1,61 @@
+SC.loadPackage({ 'WidgetBackgroundColor': {
+
+    comment: 'I am the background color widget.',
+
+    traits:  ['ColorPickerWidget'],
+
+    sharedProperties: {
+
+        widgetMenu:  { initValue: '<div class="sg-editing-widget-container"><button id="sg-editing-widget-backgroundColor" class="sg-editing-widget-button"></button></div>' }
+
+    },
+
+    methods: {
+
+    	init: { 
+    		comment: 	'I init the widget.',
+    		code: 		function(theSelection){
+
+                this.delegate('Widget', 'init', theSelection);
+
+
+                var initialColor = theSelection.get('elements')[0].get('node').style.backgroundColor,
+                    pickerLoad   = true;
+
+                this.do('initColorPickerWidget', {
+
+                    theSelection: theSelection,
+
+                    initialColor: initialColor,
+
+                    setCallback: function(colorCode){
+
+                        if(pickerLoad){
+                            return pickerLoad = false;
+                        }
+
+                        var elements = theSelection.get('elements')
+
+                        for(var i = 0, l = elements.length; i < l; i++){
+
+                            elements[i].get('node').style.backgroundColor = colorCode;
+
+                        }
+
+                    }
+
+
+                });
+
+
+
+
+    		}
+
+    	}
+
+
+    }
+
+
+}});

+ 65 - 0
src/allplatforms/classes/WidgetBorder.js

@@ -0,0 +1,65 @@
+SC.loadPackage({ 'WidgetBorder': {
+
+    comment: 'I am the widget controlling border styles.',
+
+    traits:  ['SliderWidget'],
+
+    sharedProperties: {
+
+        widgetMenu:  { initValue: '<div class="sg-editing-widget-container"><button id="sg-editing-widget-border" class="sg-editing-widget-button"></button></div>' }
+
+    },
+
+    
+
+    methods: {
+
+        init: { 
+            comment:    'I init the widget.',
+            code:       function(theSelection){
+
+                this.delegate('Widget', 'init', theSelection);
+
+                
+                this.do('initSliderWidget', {
+
+                    theSelection:   theSelection,
+
+                    startValue:     theSelection.get('elements').length === 1
+                                    ? (theSelection.get('elements')[0].get('borderWidth') / 24)
+                                    : 0,
+
+                    setCallback:    function(sliderVal){
+                                        
+                                        var borderVal = Math.round(sliderVal * 24),
+                                            elements  = theSelection.get('elements');
+
+                                    
+                                        for(var i = 0, l = elements.length; i < l; i++){
+
+                                            if(borderVal !== 0 && !elements[i].get('borderColor')){
+                                                elements[i].set({ borderColor: '#000000' });
+                                            }
+                                            elements[i].set({ borderWidth: borderVal });
+                                            elements[i].set({ 
+                                                width:  elements[i].get('width'),
+                                                height: elements[i].get('height')
+                                            });
+                                            
+                                        }
+
+                                    
+
+                                    }
+                });
+
+
+            }
+
+        }
+
+
+    }
+
+
+}});

+ 61 - 0
src/allplatforms/classes/WidgetBorderColor.js

@@ -0,0 +1,61 @@
+SC.loadPackage({ 'WidgetBorderColor': {
+
+    comment: 'I am the widget controlling border color.',
+
+    traits:  ['ColorPickerWidget'],
+
+    sharedProperties: {
+
+        widgetMenu:  { initValue: '<div class="sg-editing-widget-container"><button id="sg-editing-widget-borderColor" class="sg-editing-widget-button"></button></div>' }
+
+    },
+
+
+
+    methods: {
+
+    	init: { 
+    		comment: 	'I init the widget.',
+    		code: 		function(theSelection){
+
+                this.delegate('Widget', 'init', theSelection);
+
+                var initialColor = theSelection.get('elements')[0].get('borderColor'),
+                    pickerLoad   = true;
+
+
+                this.do('initColorPickerWidget', {
+
+                    theSelection: theSelection,
+
+                    initialColor: initialColor,
+
+                    setCallback: function(colorCode){
+
+                        if(pickerLoad){
+                            return pickerLoad = false;
+                        }
+
+                        var elements = theSelection.get('elements')
+
+                        for(var i = 0, l = elements.length; i < l; i++){
+
+                            elements[i].set({ borderColor: colorCode });
+
+                        }
+
+                    }
+
+
+                })               
+
+
+    		}
+
+    	}
+
+
+    }
+
+
+}});

+ 66 - 0
src/allplatforms/classes/WidgetBorderRadius.js

@@ -0,0 +1,66 @@
+SC.loadPackage({ 'WidgetBorderRadius': {
+
+    comment: 'I am the widget controlling border radius.',
+
+    traits:  ['SliderWidget'],
+
+    sharedProperties: {
+
+        widgetMenu:  { initValue: '<div class="sg-editing-widget-container"><button id="sg-editing-widget-borderRadius" class="sg-editing-widget-button"></button></div>' }
+
+    },
+
+    methods: {
+
+    	init: { 
+    		comment: 	'I init the widget.',
+    		code: 		function(theSelection){
+
+                this.delegate('Widget', 'init', theSelection);
+
+                var startBorderRadius = parseInt(theSelection.get('elements')[0].get('contentNode').style.borderRadius);
+                    startBorderRadius = theSelection.get('elements').length === 1
+                                        ? isNaN(startBorderRadius) ? 0 : startBorderRadius
+                                        : 0;
+                
+                this.do('initSliderWidget', {
+
+                    theSelection:   theSelection,
+
+                    startValue:     (startBorderRadius / 50),
+
+                    setCallback:    function(sliderVal){
+                                        
+                                        var borderRadiusVal = Math.round(sliderVal * 50),
+                                            elements  = theSelection.get('elements');
+
+                                        if(borderRadiusVal === 0){
+
+                                            for(var i = 0, l = elements.length; i < l; i++){
+                                                elements[i].get('contentNode').style.borderRadius = '';
+                                            }
+
+                                        }else{
+
+                                            for(var i = 0, l = elements.length; i < l; i++){
+                                                elements[i].get('contentNode').style.borderRadius = borderRadiusVal + '%';
+                                            }
+
+                                        }
+                                        
+
+                                    
+
+                                    }
+                });
+
+
+    		}
+
+    	}
+
+
+    }
+
+
+}});

+ 33 - 0
src/allplatforms/classes/WidgetCopy.js

@@ -0,0 +1,33 @@
+SC.loadPackage({ 'WidgetCopy': {
+
+    comment: 'I am the copy widget.',
+
+    traits:  ['Widget'],
+
+    sharedProperties: {
+
+        widgetMenu:  { initValue: '<div class="sg-editing-widget-container"><button id="sg-editing-widget-copy" class="sg-editing-widget-button"></button></div>' }
+
+    },
+
+    methods: {
+
+    	init: { 
+    		comment: 	'I init the widget.',
+    		code: 		function(theSelection){
+
+                this.delegate('Widget', 'init', theSelection);
+                this.set({ isActionButton: true });
+
+
+
+
+    		}
+
+    	}
+
+
+    }
+
+
+}});

+ 51 - 0
src/allplatforms/classes/WidgetDelete.js

@@ -0,0 +1,51 @@
+SC.loadPackage({ 'WidgetDelete': {
+
+    comment: 'I am the delete widget.',
+
+    traits:  ['Widget'],
+
+    sharedProperties: {
+
+        widgetMenu:  { initValue: '<div class="sg-editing-widget-container"><button id="sg-editing-widget-delete" class="sg-editing-widget-button"></button></div>' }
+
+    },
+
+    methods: {
+
+    	init: { 
+    		comment: 	'I init the widget.',
+    		code: 		function(theSelection){
+
+                this.delegate('Widget', 'init', theSelection);
+                this.set({ isActionButton: true });
+
+                var self = this;
+
+                this.get('widgetButton').addEventListener('mouseup', function(evt){
+
+                    self.set({ isWidgetActive: false });
+
+
+                    var elements = self.get('selection').get('elements').slice();
+                    self.get('selection').do('clearAll');
+
+                    for(var i = 0, l = elements.length; i < l; i++){
+                        
+                        SuperGlue.get('document').do('removeElement', elements[i]);
+
+                    }
+
+
+
+                }, false);
+
+
+    		}
+
+    	}
+
+
+    }
+
+
+}});

+ 72 - 0
src/allplatforms/classes/WidgetEditHTML.js

@@ -0,0 +1,72 @@
+SC.loadPackage({ 'WidgetEditHTML': {
+
+    comment: 'I am the widget to start editing the html manually.',
+
+    traits:  ['Widget'],
+
+    sharedProperties: {
+
+        widgetMenu:  { initValue: '<div class="sg-editing-widget-container"><button id="sg-editing-widget-editHTML" class="sg-editing-widget-button"></button></div>' }
+
+    },
+
+    methods: {
+
+    	init: { 
+    		comment: 	'I init the widget.',
+    		code: 		function(theSelection){
+
+                this.delegate('Widget', 'init', theSelection);
+                this.set({ isActionButton: true });
+
+
+                var self = this,
+                    onMouseUp = function(evt){
+
+                        var elements = SuperGlue.get('selection').get('elements');
+
+                        for(var rawHtml, i = 0, l = elements.length; i < l; i++){
+
+                            rawHtml = elements[i].get('contentNode').innerHTML.split('\n');
+                            rawHtml.forEach(function(line, idx, array){ array[idx] = line.trim(); });
+                            rawHtml = rawHtml.join('\n');
+                            
+                            SuperGlue.get('windowManager').do('createWindow', {
+
+                                class:      'HTMLEditor',
+                                
+                                html:       rawHtml,
+                                
+                                callback:   (function(){
+                                                var contentNode = elements[i].get('contentNode');
+                                                return function(aHTMLString){
+                                                    contentNode.innerHTML = aHTMLString;
+                                                }
+                                            }).call(this),
+
+                                top:        elements[i].get('top') + 30 - document.body.scrollTop,
+                                left:       (SuperGlue.get('document').get('layout').centered 
+                                                ? (document.documentElement.clientWidth - SuperGlue.get('document').get('layout').width) / 2
+                                                : 0) 
+                                            +   elements[i].get('left') + 30 - document.body.scrollLeft,
+                                width:      600,
+                                height:     300
+                            });
+
+                        }
+
+
+                    };
+
+                this.get('widgetButton').addEventListener('mouseup', onMouseUp, false)
+
+
+    		}
+
+    	}
+
+
+    }
+
+
+}});

+ 136 - 0
src/allplatforms/classes/WidgetIFrame.js

@@ -0,0 +1,136 @@
+SC.loadPackage({ 'WidgetIFrame': {
+
+    comment: 'I am the widget controlling an IFrame element.',
+
+    traits:  ['Widget'],
+
+    sharedProperties: {
+
+        widgetMenu:  { initValue: '<div class="sg-editing-widget-container"><button id="sg-editing-widget-iframe" class="sg-editing-widget-button"></button></div>' },
+
+        widgetPanel: { initValue:   '<div id="sg-editing-widget-iframe-panel">'
+                                        +'<div class="sg-editing-widget-panel">'
+                                            +'<div class="sg-widget-triangle-up"></div>'
+                                            +'<input  id="sg-editing-widget-iframe-input" type="text"></input>'
+                                            +'<button id="sg-editing-widget-iframe-chooseFile" class="sg-editing-widget-button"></button>'
+                                            +'<button id="sg-editing-widget-iframe-clear" class="sg-editing-widget-button"></button>'
+                                        +'</div>'
+                                    +'</div>' }
+
+    },
+
+    properties: {
+
+        widgetPanel:    { comment: 'I store the DOMElement containing the panel of controls of the widget.' },
+
+        isWidgetActive: { comment: 'Wether the widget is active.',
+                          transform: function(aBoolean){
+
+                                if(aBoolean){
+
+                                    if(this.get('widgetPanel').parentNode !== this.get('widgetMenu')){
+                                        this.get('widgetMenu').appendChild(this.get('widgetPanel'));
+                                    }
+                                    this.get('widgetMenu').classList.add('active');
+
+                                    var elements = this.get('selection').get('elements')
+                                    if(elements.length === 1){
+                                        this.get('widgetPanel').querySelector('#sg-editing-widget-iframe-input').value = (
+                                            elements[0].get('contentNode').firstElementChild.getAttribute('src')
+                                        );
+                                    }
+
+                                }else{
+
+                                    if(this.get('widgetPanel').parentNode === this.get('widgetMenu')){
+                                        this.get('widgetMenu').removeChild(this.get('widgetPanel'));
+                                    }
+                                    this.get('widgetMenu').classList.remove('active');
+                                
+                                }
+                                return aBoolean
+                          }
+                        }
+
+    },
+
+    methods: {
+
+    	init: { 
+    		comment: 	'I init the widget.',
+    		code: 		function(theSelection){
+
+                this.delegate('Widget', 'init', theSelection);
+
+
+                var self = this,
+                    widgetPanel = (new DOMParser()).parseFromString(this.class.get('widgetPanel'), 'text/html').body.firstChild;
+                
+
+
+                widgetPanel.querySelector('#sg-editing-widget-iframe-input').addEventListener('mouseup', function(evt){
+                    theSelection.set({ activeWidget: self });
+                    evt.stopPropagation();
+                }, false);
+
+                widgetPanel.querySelector('#sg-editing-widget-iframe-input').addEventListener('input', function(evt){
+                    self.do('setIFrameSrc', this.value)
+                }, false);
+
+                widgetPanel.querySelector('#sg-editing-widget-iframe-chooseFile').addEventListener('mouseup', function(evt){
+                    theSelection.set({ activeWidget: self });
+                    evt.stopPropagation();
+
+                    var pathParser = document.createElement('a');
+                    pathParser.href = widgetPanel.querySelector('#sg-editing-widget-iframe-input').value;
+
+                    SuperGlue.get('fileManager').do('chooseFile', {
+                        oldPath:  pathParser.pathName,
+                        callback: function(srcPath){
+                                        widgetPanel.querySelector('#sg-editing-widget-iframe-input').value = srcPath;
+                                        self.do('setIFrameSrc', srcPath);
+                                    }
+                    });
+                }, false);
+
+                widgetPanel.querySelector('#sg-editing-widget-iframe-clear').addEventListener('mouseup', function(evt){
+                    theSelection.set({ activeWidget: self });
+                    evt.stopPropagation();
+
+                    widgetPanel.querySelector('#sg-editing-widget-iframe-input').value = '';
+                    self.do('setIFrameSrc', '');
+                }, false);
+
+
+                this.set({ 
+                    widgetPanel: widgetPanel
+                });
+
+
+
+    		}
+
+    	},
+
+        setIFrameSrc: {
+            comment: 'I set the src for an iFrameElement.',
+            code: function(srcURL){
+
+                var elements     = this.get('selection').get('elements');
+
+                for(var i = 0, l = elements.length; i < l; i++){
+
+                    elements[i].get('contentNode').firstElementChild.src = srcURL;
+
+                }
+
+
+            }
+        }
+
+
+
+    }
+
+
+}});

+ 103 - 0
src/allplatforms/classes/WidgetImgDimensions.js

@@ -0,0 +1,103 @@
+SC.loadPackage({ 'WidgetImgDimensions': {
+
+    comment: 'I am the widget controlling border styles.',
+
+    traits:  ['Widget'],
+
+    sharedProperties: {
+
+        widgetMenu:  { initValue: '<div class="sg-editing-widget-container"><button id="sg-editing-widget-imgDimensions" class="sg-editing-widget-button"></button></div>' },
+
+        widgetPanel: { initValue: '<div id="sg-editing-widget-imgDimensions-panel">'
+                                        +'<div class="sg-editing-widget-panel">'
+                                            +'<div class="sg-widget-triangle-up"></div>'
+                                            +'<button id="sg-editing-widget-imgDimensions-tile"               data-superglue-imgDimensions="tile" class="sg-editing-widget-button"></button>'
+                                            +'<button id="sg-editing-widget-imgDimensions-tileX"              data-superglue-imgDimensions="tileX" class="sg-editing-widget-button"></button>'
+                                            +'<button id="sg-editing-widget-imgDimensions-tileY"              data-superglue-imgDimensions="tileY" class="sg-editing-widget-button"></button>'
+                                            +'<button id="sg-editing-widget-imgDimensions-stretch"            data-superglue-imgDimensions="stretch" class="sg-editing-widget-button"></button>'
+                                            +'<button id="sg-editing-widget-imgDimensions-stretchAspectRatio" data-superglue-imgDimensions="stretchAspectRatio" class="sg-editing-widget-button"></button>'
+                                            +'<button id="sg-editing-widget-imgDimensions-aspectRatio"        data-superglue-imgDimensions="aspectRatio" class="sg-editing-widget-button"></button>'
+                                        +'</div>'
+                                    +'</div>' 
+                     }
+
+    },
+
+
+    properties: {
+
+        widgetPanel:    { comment: 'I store the DOMElement containing the panel of controls of the widget.' },
+
+        isWidgetActive: { comment: 'Wether the widget is active.',
+                          transform: function(aBoolean){
+
+                                if(aBoolean){
+
+                                    if(this.get('widgetPanel').parentNode !== this.get('widgetMenu')){
+                                        this.get('widgetMenu').appendChild(this.get('widgetPanel'));
+                                    }
+                                    this.get('widgetMenu').classList.add('active');
+
+
+                                }else{
+
+                                    if(this.get('widgetPanel').parentNode === this.get('widgetMenu')){
+                                        this.get('widgetMenu').removeChild(this.get('widgetPanel'));
+                                    }
+                                    this.get('widgetMenu').classList.remove('active');
+                                
+                                }
+                                return aBoolean
+                          }
+                        }
+
+    },
+
+
+    methods: {
+
+    	init: { 
+    		comment: 	'I init the widget.',
+    		code: 		function(theSelection){
+
+                this.delegate('Widget', 'init', theSelection);
+
+                var self = this,
+                    widgetPanel = (new DOMParser()).parseFromString(this.class.get('widgetPanel'), 'text/html').body.firstChild,
+                
+                    onMouseUp = function(evt){
+
+                        var newDim   = this.getAttribute('data-superglue-imgDimensions'),
+                            elements = theSelection.get('elements');
+
+                        for(var i = 0, l = elements.length; i < l; i++){
+                            elements[i].set({ dimensions: newDim });
+                        }
+
+                        evt.stopPropagation()
+
+                    },
+
+                    buttons = widgetPanel.querySelectorAll('.sg-editing-widget-button');
+
+
+                for(var i = buttons.length - 1; i >= 0; i--){
+                    buttons[i].addEventListener('mouseup', onMouseUp, false)
+                }
+                
+
+
+                this.set({ 
+                    widgetPanel: widgetPanel
+                });
+
+
+    		}
+
+    	}
+
+
+    }
+
+
+}});

+ 158 - 0
src/allplatforms/classes/WidgetImgLink.js

@@ -0,0 +1,158 @@
+SC.loadPackage({ 'WidgetImgLink': {
+
+    comment: 'I am the widget controlling a href link on an ImageElement.',
+
+    traits:  ['Widget'],
+
+    sharedProperties: {
+
+        widgetMenu:  { initValue: '<div class="sg-editing-widget-container"><button id="sg-editing-widget-imgLink" class="sg-editing-widget-button"></button></div>' },
+
+        widgetPanel: { initValue:   '<div id="sg-editing-widget-imgLink-panel">'
+                                        +'<div class="sg-editing-widget-panel">'
+                                            +'<div class="sg-widget-triangle-up"></div>'
+                                            +'<input  id="sg-editing-widget-imgLink-input" type="text"></input>'
+                                            +'<button id="sg-editing-widget-imgLink-chooseFile" class="sg-editing-widget-button"></button>'
+                                            +'<button id="sg-editing-widget-imgLink-clear" class="sg-editing-widget-button"></button>'
+                                        +'</div>'
+                                    +'</div>' }
+
+    },
+
+    properties: {
+
+        widgetPanel:    { comment: 'I store the DOMElement containing the panel of controls of the widget.' },
+
+        isWidgetActive: { comment: 'Wether the widget is active.',
+                          transform: function(aBoolean){
+
+                                if(aBoolean){
+
+                                    if(this.get('widgetPanel').parentNode !== this.get('widgetMenu')){
+                                        this.get('widgetMenu').appendChild(this.get('widgetPanel'));
+                                    }
+                                    this.get('widgetMenu').classList.add('active');
+
+                                    var elements = this.get('selection').get('elements')
+                                    if(elements.length === 1){
+                                        this.get('widgetPanel').querySelector('#sg-editing-widget-imgLink-input').value = (
+                                            elements[0].get('contentNode').firstElementChild.getAttribute('href')
+                                        );
+                                    }
+
+                                }else{
+
+                                    if(this.get('widgetPanel').parentNode === this.get('widgetMenu')){
+                                        this.get('widgetMenu').removeChild(this.get('widgetPanel'));
+                                    }
+                                    this.get('widgetMenu').classList.remove('active');
+                                
+                                }
+                                return aBoolean
+                          }
+                        }
+
+    },
+
+    methods: {
+
+        init: { 
+            comment:    'I init the widget.',
+            code:       function(theSelection){
+
+                this.delegate('Widget', 'init', theSelection);
+
+
+                var self = this,
+                    widgetPanel = (new DOMParser()).parseFromString(this.class.get('widgetPanel'), 'text/html').body.firstChild;
+                
+
+
+                widgetPanel.querySelector('#sg-editing-widget-imgLink-input').addEventListener('mouseup', function(evt){
+                    theSelection.set({ activeWidget: self });
+                    evt.stopPropagation();
+                }, false);
+
+                widgetPanel.querySelector('#sg-editing-widget-imgLink-input').addEventListener('input', function(evt){
+                    self.do('setImgLink', this.value)
+                }, false);
+
+                widgetPanel.querySelector('#sg-editing-widget-imgLink-chooseFile').addEventListener('mouseup', function(evt){
+                    theSelection.set({ activeWidget: self });
+                    evt.stopPropagation();
+
+                    var pathParser = document.createElement('a');
+                    pathParser.href = widgetPanel.querySelector('#sg-editing-widget-imgLink-input').value;
+
+                    SuperGlue.get('fileManager').do('chooseFile', {
+                        oldPath:  pathParser.pathName,
+                        callback: function(linkPath){
+                                        widgetPanel.querySelector('#sg-editing-widget-imgLink-input').value = linkPath;
+                                        self.do('setImgLink', linkPath);
+                                    }
+                    })
+                }, false);
+
+                widgetPanel.querySelector('#sg-editing-widget-imgLink-clear').addEventListener('mouseup', function(evt){
+                    theSelection.set({ activeWidget: self });
+                    evt.stopPropagation();
+
+                    widgetPanel.querySelector('#sg-editing-widget-imgLink-input').value = '';
+                    self.do('setImgLink', '');
+                }, false);
+
+
+                this.set({ 
+                    widgetPanel: widgetPanel
+                });
+
+            }
+
+        },
+
+        setImgLink: {
+            comment: 'I set a link on a imgElement',
+            code: function(linkURL){
+
+                var elements     = this.get('selection').get('elements'),
+                    contentNode;
+
+                for(var i = 0, l = elements.length; i < l; i++){
+
+                    contentNode = elements[i].get('contentNode');
+
+                    if(linkURL === ''){
+
+                        if(contentNode.firstElementChild.tagName === 'A'){
+                            var imgElem = contentNode.firstElementChild.firstElementChild,
+                                aElem   = contentNode.firstElementChild;
+                            contentNode.insertBefore(imgElem, aElem);
+                            contentNode.removeChild(aElem);
+                        }
+
+                    }else{
+
+                        if(contentNode.firstElementChild.tagName !== 'A'){
+                            var imgElem = contentNode.firstElementChild,
+                                aElem   = document.createElement('a');
+                                aElem.setAttribute('href', linkURL);
+                            contentNode.insertBefore(aElem, imgElem);
+                            aElem.appendChild(imgElem);
+                        }else{
+                            contentNode.firstElementChild.setAttribute('href', linkURL);
+                        }
+
+                    }
+
+
+                }
+
+
+            }
+        }
+
+
+    }
+
+
+}});

+ 135 - 0
src/allplatforms/classes/WidgetImgSrc.js

@@ -0,0 +1,135 @@
+SC.loadPackage({ 'WidgetImgSrc': {
+
+    comment: 'I am the widget controlling the image source of an ImageElement.',
+
+    traits:  ['Widget'],
+
+    sharedProperties: {
+
+        widgetMenu:  { initValue: '<div class="sg-editing-widget-container"><button id="sg-editing-widget-imgSrc" class="sg-editing-widget-button"></button></div>' },
+
+        widgetPanel: { initValue:   '<div id="sg-editing-widget-imgSrc-panel">'
+                                        +'<div class="sg-editing-widget-panel">'
+                                            +'<div class="sg-widget-triangle-up"></div>'
+                                            +'<input  id="sg-editing-widget-imgSrc-input" type="text"></input>'
+                                            +'<button id="sg-editing-widget-imgSrc-chooseFile" class="sg-editing-widget-button"></button>'
+                                            +'<button id="sg-editing-widget-imgSrc-clear" class="sg-editing-widget-button"></button>'
+                                        +'</div>'
+                                    +'</div>' }
+
+    },
+
+    properties: {
+
+        widgetPanel:    { comment: 'I store the DOMElement containing the panel of controls of the widget.' },
+
+        isWidgetActive: { comment: 'Wether the widget is active.',
+                          transform: function(aBoolean){
+
+                                if(aBoolean){
+
+                                    if(this.get('widgetPanel').parentNode !== this.get('widgetMenu')){
+                                        this.get('widgetMenu').appendChild(this.get('widgetPanel'));
+                                    }
+                                    this.get('widgetMenu').classList.add('active');
+
+                                    var elements = this.get('selection').get('elements');
+                                        
+                                    if(elements.length === 1){
+                                        this.get('widgetPanel').querySelector('#sg-editing-widget-imgSrc-input').value = elements[0].get('imgSource');
+                                    }
+
+                                }else{
+
+                                    if(this.get('widgetPanel').parentNode === this.get('widgetMenu')){
+                                        this.get('widgetMenu').removeChild(this.get('widgetPanel'));
+                                    }
+                                    this.get('widgetMenu').classList.remove('active');
+                                
+                                }
+                                return aBoolean
+                          }
+                        }
+
+    },
+
+    methods: {
+
+    	init: { 
+    		comment: 	'I init the widget.',
+    		code: 		function(theSelection){
+
+                this.delegate('Widget', 'init', theSelection);
+
+                var self = this,
+                    widgetPanel = (new DOMParser()).parseFromString(this.class.get('widgetPanel'), 'text/html').body.firstChild;
+                
+
+
+                widgetPanel.querySelector('#sg-editing-widget-imgSrc-input').addEventListener('mouseup', function(evt){
+                    theSelection.set({ activeWidget: self });
+                    evt.stopPropagation();
+                }, false);
+
+                widgetPanel.querySelector('#sg-editing-widget-imgSrc-input').addEventListener('input', function(evt){
+                    self.do('setImgSrc', this.value)
+                }, false);
+
+                widgetPanel.querySelector('#sg-editing-widget-imgSrc-chooseFile').addEventListener('mouseup', function(evt){
+                    theSelection.set({ activeWidget: self });
+                    evt.stopPropagation();
+
+
+                    var pathParser = document.createElement('a');
+                    pathParser.href = widgetPanel.querySelector('#sg-editing-widget-imgSrc-input').value;
+
+                    SuperGlue.get('fileManager').do('chooseFile', {
+                        oldPath:  pathParser.pathName,
+                        callback: function(srcPath){
+                                        widgetPanel.querySelector('#sg-editing-widget-imgSrc-input').value = srcPath;
+                                        self.do('setImgSrc', srcPath);
+                                    }
+                    });
+                }, false);
+
+                widgetPanel.querySelector('#sg-editing-widget-imgSrc-clear').addEventListener('mouseup', function(evt){
+                    theSelection.set({ activeWidget: self });
+                    evt.stopPropagation();
+
+                    widgetPanel.querySelector('#sg-editing-widget-imgSrc-input').value = '';
+                    self.do('setImgSrc', '');
+                }, false);
+
+
+                this.set({ 
+                    widgetPanel: widgetPanel
+                });
+
+
+    		}
+
+    	},
+
+        setImgSrc: {
+            comment: 'I set the source of the imgElement',
+            code: function(srcURL){
+
+                var elements     = this.get('selection').get('elements'),
+                    contentNode, imgElement;
+
+                for(var i = 0, l = elements.length; i < l; i++){
+
+                    elements[i].set({ imgSource: srcURL });
+
+
+                }
+
+
+            }
+        }
+
+
+    }
+
+
+}});

+ 135 - 0
src/allplatforms/classes/WidgetLayerBottom.js

@@ -0,0 +1,135 @@
+SC.loadPackage({ 'WidgetLayerBottom': {
+
+    comment: 'I am the widget that brings the current selection to the bottom of layering.',
+
+    traits:  ['Widget'],
+
+    sharedProperties: {
+
+        widgetMenu:  { initValue: '<div class="sg-editing-widget-container"><button id="sg-editing-widget-layerBottom" class="sg-editing-widget-button"></button></div>' }
+
+    },
+
+    methods: {
+
+    	init: { 
+    		comment: 	'I init the widget.',
+    		code: 		function(theSelection){
+
+                this.delegate('Widget', 'init', theSelection);
+                this.set({ isActionButton: true });
+
+                var self = this,
+                    onMouseUp = function(evt){
+
+                        var modifierKey      = evt.shiftKey || evt.ctrlKey,
+                            elements         = SuperGlue.get('selection').get('elements'),
+                            pageContainer    = SuperGlue.get('document').get('pageContainer'),
+                            documentChilds   = SuperGlue.get('document').get('children'),
+                            selectionHasGaps = false,
+                            elementsToMove   = [];
+
+
+                        if(documentChilds.length <= 1){ 
+                            return;
+                        }
+
+
+                        if(elements.length === 1){
+
+                            if(modifierKey){
+
+                                SuperGlue.get('document').do('layerDown', elements[0]);
+
+                            }else{
+
+                                for(var i = documentChilds.indexOf(elements[0]);
+                                    i > 0; i--){
+                                    SuperGlue.get('document').do('layerDown', elements[0]);
+                                }
+
+                            }
+                            
+
+                        }else{
+
+
+                            for(var i = 0, l = elements.length; i < l; i++){
+                                elementsToMove.push({
+                                    index: documentChilds.indexOf(elements[i]), 
+                                    child: elements[i],
+                                    gapBelow: null
+                                });
+                            }
+
+                            elementsToMove.sort(function(a,b){ return a.index > b.index; });
+
+                            for(var i = 1, l = elementsToMove.length; i < l; i++){
+                                if(elementsToMove[i].index !== elementsToMove[i - 1].index + 1){
+                                    selectionHasGaps = true;
+                                    break;
+                                }
+                            }
+
+
+                            if(selectionHasGaps){
+
+                                for(var i = 1, l = elementsToMove.length; i < l; i++){
+                                    elementsToMove[i].gapBelow = elementsToMove[i].index - i - elementsToMove[0].index;
+                                }
+
+                                for(var i = 1, l = elementsToMove.length; i < l; i++){
+                                    for(var k = elementsToMove[i].gapBelow; k > 0; k--){
+                                        SuperGlue.get('document').do('layerDown', elementsToMove[i].child);
+                                    }
+                                }
+
+
+                            }else{
+
+                                if(elementsToMove[0].index === 0){
+                                    return;
+                                }
+
+                                if(modifierKey){
+
+                                    for(var i = 0, l = elementsToMove.length; i < l; i++){
+                                        SuperGlue.get('document').do('layerDown', elementsToMove[i].child);
+                                    }
+
+                                }else{
+
+                                    for(var n = elementsToMove[0].index;
+                                        n > 0; n--){
+
+                                        for(var i = 0, l = elementsToMove.length; i < l; i++){
+                                            SuperGlue.get('document').do('layerDown', elementsToMove[i].child);
+                                        }
+
+                                    }
+
+                                }
+                                
+
+                            }
+
+
+
+                        }
+
+
+
+                    };
+
+                this.get('widgetButton').addEventListener('mouseup', onMouseUp, false)
+
+
+    		}
+
+    	}
+
+
+    }
+
+
+}});

+ 141 - 0
src/allplatforms/classes/WidgetLayerTop.js

@@ -0,0 +1,141 @@
+SC.loadPackage({ 'WidgetLayerTop': {
+
+    comment: 'I am the widget that brings the current selection to the top of layering.',
+
+    traits:  ['Widget'],
+
+    sharedProperties: {
+
+        widgetMenu:  { initValue: '<div class="sg-editing-widget-container"><button id="sg-editing-widget-layerTop" class="sg-editing-widget-button"></button></div>' }
+
+    },
+
+    methods: {
+
+    	init: { 
+    		comment: 	'I init the widget.',
+    		code: 		function(theSelection){
+
+                this.delegate('Widget', 'init', theSelection);
+                this.set({ isActionButton: true });
+
+
+                var self = this,
+                    onMouseUp = function(evt){
+
+                        var modifierKey      = evt.shiftKey || evt.ctrlKey,
+                            elements         = SuperGlue.get('selection').get('elements'),
+                            pageContainer    = SuperGlue.get('document').get('pageContainer'),
+                            documentChilds   = SuperGlue.get('document').get('children'),
+                            selectionHasGaps = false,
+                            elementsToMove   = [];
+
+
+                        if(documentChilds.length <= 1){ 
+                            return;
+                        }
+
+
+                        if(elements.length === 1){
+
+                            if(modifierKey){
+
+                                SuperGlue.get('document').do('layerUp', elements[0]);
+
+                            }else{
+
+                                for(var i = documentChilds.indexOf(elements[0]), l = documentChilds.length;
+                                    i < l; i++){
+                                    SuperGlue.get('document').do('layerUp', elements[0]);
+                                }
+
+                            }
+                            
+                            
+
+                        }else{
+
+
+                            for(var i = 0, l = elements.length; i < l; i++){
+                                elementsToMove.push({
+                                    index: documentChilds.indexOf(elements[i]), 
+                                    child: elements[i],
+                                    gapAbove: null
+                                });
+                            }
+
+                            elementsToMove.sort(function(a,b){ return a.index < b.index; });
+
+                            for(var i = 1, l = elementsToMove.length; i < l; i++){
+                                if(elementsToMove[i].index !== elementsToMove[i - 1].index - 1){
+                                    selectionHasGaps = true;
+                                    break;
+                                }
+                            }
+
+
+
+                            if(selectionHasGaps){
+
+                                for(var i = 1, l = elementsToMove.length; i < l; i++){
+                                    elementsToMove[i].gapAbove = elementsToMove[0].index - i - elementsToMove[i].index;
+                                }
+
+                                for(var i = 1, l = elementsToMove.length; i < l; i++){
+                                    for(var k = elementsToMove[i].gapAbove; k > 0; k--){
+                                        SuperGlue.get('document').do('layerUp', elementsToMove[i].child);
+                                    }
+                                }
+
+
+                            }else{
+
+
+                                if(elementsToMove[0].index >= documentChilds.length - 1){
+                                    return;
+                                }
+
+                                if(modifierKey){
+
+                                    for(var i = 0, l = elementsToMove.length; i < l; i++){
+                                        SuperGlue.get('document').do('layerUp', elementsToMove[i].child);
+                                    }
+
+                                }else{
+
+                                    for(var n = elementsToMove[0].index + 1, k = documentChilds.length;
+                                        n < k; n++){
+
+                                        for(var i = 0, l = elementsToMove.length; i < l; i++){
+                                            SuperGlue.get('document').do('layerUp', elementsToMove[i].child);
+                                        }
+
+                                    }
+
+                                }
+
+
+                            }
+
+
+
+                        }
+
+                        
+
+                    };
+
+                this.get('widgetButton').addEventListener('mouseup', onMouseUp, false)
+
+
+
+
+    		}
+
+    	}
+
+
+    }
+
+
+}});

+ 74 - 0
src/allplatforms/classes/WidgetLock.js

@@ -0,0 +1,74 @@
+SC.loadPackage({ 'WidgetLock': {
+
+    comment: 'I am the Lock widget to lock a multiple selection to one group.',
+
+    traits:  ['Widget'],
+
+    sharedProperties: {
+
+        widgetMenu:  { initValue: '<div class="sg-editing-widget-container"><button id="sg-editing-widget-lock" class="sg-editing-widget-button"></button></div>' }
+
+    },
+
+    properties: {
+        locked:     { comment:    'Am I currently locked?',
+                      transform:  function(aBoolean){
+                            if(aBoolean){
+                                this.get('widgetButton').classList.add('unlocked');
+                            }else{
+                                this.get('widgetButton').classList.remove('unlocked');
+                            }
+                            return aBoolean;
+                      }
+                    }
+    },
+
+    methods: {
+
+    	init: { 
+    		comment: 	'I init the widget.',
+    		code: 		function(theSelection){
+
+                this.delegate('Widget', 'init', theSelection);
+                this.set({ isActionButton: true });
+
+                var self = this,
+                    onMouseUp = function(evt){
+
+                        if(self.get('locked')){
+
+                            for(var myElements = self.get('selection').get('elements'),
+                                    i = 0, l = myElements.length; i < l; i++){
+
+                                myElements[i].set({ group: null });
+
+                            }
+
+                        }else{
+
+                            for(var groupID    = Date.now(),
+                                    myElements = self.get('selection').get('elements'),
+                                    i = 0, l = myElements.length; i < l; i++){
+
+                                myElements[i].set({ group: groupID });
+
+                            }
+
+                        }
+
+                        self.get('selection').do('updateLockGroup');
+
+                    };
+
+                this.get('widgetButton').addEventListener('mouseup', onMouseUp, false)
+
+
+    		}
+
+    	}
+
+
+    }
+
+
+}});

+ 66 - 0
src/allplatforms/classes/WidgetOpacity.js

@@ -0,0 +1,66 @@
+SC.loadPackage({ 'WidgetOpacity': {
+
+    comment: 'I am the widget controlling the opacity of elements.',
+
+    traits:  ['SliderWidget'],
+
+    sharedProperties: {
+
+        widgetMenu:  { initValue: '<div class="sg-editing-widget-container"><button id="sg-editing-widget-opacity" class="sg-editing-widget-button"></button></div>' }
+
+    },
+
+    methods: {
+
+    	init: { 
+    		comment: 	'I init the widget.',
+    		code: 		function(theSelection){
+
+                this.delegate('Widget', 'init', theSelection);
+
+                var startOpacity = parseFloat(theSelection.get('elements')[0].get('contentNode').style.opacity);
+                    startOpacity = theSelection.get('elements').length === 1
+                                    ? isNaN(startOpacity) ? 1 : startOpacity
+                                    : 1;
+                
+                this.do('initSliderWidget', {
+
+                    theSelection:   theSelection,
+
+                    startValue:     (1 - startOpacity),
+
+                    setCallback:    function(sliderVal){
+                                        
+                                        var opacityVal = 1 - sliderVal,
+                                            elements  = theSelection.get('elements');
+
+                                        if(opacityVal === 1){
+
+                                            for(var i = 0, l = elements.length; i < l; i++){
+                                                elements[i].get('contentNode').style.opacity = '';
+                                            }
+
+                                        }else{
+
+                                            for(var i = 0, l = elements.length; i < l; i++){
+                                                elements[i].get('contentNode').style.opacity = opacityVal;
+                                            }
+
+                                        }
+                                        
+
+                                    
+
+                                    }
+                });
+
+
+    		}
+
+    	}
+
+
+    }
+
+
+}});

+ 82 - 0
src/allplatforms/classes/WidgetPadding.js

@@ -0,0 +1,82 @@
+SC.loadPackage({ 'WidgetPadding': {
+
+    comment: 'I am the widget controlling padding of elements.',
+
+    traits:  ['SliderWidget'],
+
+    sharedProperties: {
+
+        widgetMenu:  { initValue: '<div class="sg-editing-widget-container"><button id="sg-editing-widget-padding" class="sg-editing-widget-button"></button></div>' }
+
+    },
+
+    
+
+    methods: {
+
+    	init: { 
+    		comment: 	'I init the widget.',
+    		code: 		function(theSelection){
+
+                this.delegate('Widget', 'init', theSelection);
+
+                this.do('initSliderWidget', {
+
+                    theSelection:   theSelection,
+
+                    startValue:     theSelection.get('elements').length === 1
+                                    ? (theSelection.get('elements')[0].get('padding') / 72)
+                                    : 0,
+
+                    setCallback:    function(sliderVal){
+                                        
+                                        var paddingVal = Math.round(sliderVal * 72),
+                                            elements  = theSelection.get('elements'),
+                                            width, height, borderWidth, appliedPaddingVal;
+
+                                    
+                                        for(var i = 0, l = elements.length; i < l; i++){
+
+                                            borderWidth = elements[i].get('borderWidth');
+                                            width       = elements[i].get('width')  - 2 * (borderWidth + elements[i].get('padding'));
+                                            height      = elements[i].get('height') - 2 * (borderWidth + elements[i].get('padding'));
+                                            
+
+                                            if(     paddingVal * 2 >= borderWidth * 2 + width
+                                                ||  paddingVal * 2 >= borderWidth * 2 + height
+                                            ){
+                                                // BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG 
+
+                                                var spaceX = Math.floor((borderWidth * 2 + width)  / 2),
+                                                    spaceY = Math.floor((borderWidth * 2 + height) / 2);
+
+                                                appliedPaddingVal = (spaceX < spaceY ? spaceX : spaceY);
+
+                                            }else{
+                                                appliedPaddingVal = paddingVal;
+                                            }
+
+
+                                            elements[i].set({ padding: appliedPaddingVal });
+                                            elements[i].set({ 
+                                                width:  elements[i].get('width'),
+                                                height: elements[i].get('height')
+                                            });
+                                            
+                                        }
+
+                                    
+
+                                    }
+                });
+
+
+    		}
+
+    	}
+
+
+    }
+
+
+}});

+ 189 - 0
src/allplatforms/classes/WidthMarkers.js

@@ -0,0 +1,189 @@
+SC.loadPackage({ 'WidthMarkers': {
+
+    comment: 'When the document has a centered layout, these markers show the left and right border of the page, and provide interaction functionality to change the width.',
+
+
+    sharedProperties: {
+
+        markerContainers:   { initValue: '<div id="sg-editing-width-marker-left"></div><div id="sg-editing-width-marker-right"></div>' }
+
+    },
+
+    properties: {
+
+        markerLeft:       { comment: 'My left marker'  },
+        markerRight:      { comment: 'My right marker' },
+        editingContainer: { comment: 'I need a reference to the editingContainer.' },
+
+        active:     { comment:      'Shall my markers be functional or not?',
+                      transform:    function(aBoolean){
+                                        if(aBoolean){
+                                            this.get('editingContainer').appendChild(this.get('markerLeft'));
+                                            this.get('editingContainer').appendChild(this.get('markerRight'))
+                                        }else{
+                                            if(    this.get('markerLeft').parentNode === this.get('editingContainer')
+                                                && this.get('markerLeft').parentNode === this.get('editingContainer')){
+                                                this.get('editingContainer').removeChild(this.get('markerLeft'));
+                                                this.get('editingContainer').removeChild(this.get('markerRight'));
+                                            }
+                                        }
+                                        return aBoolean;
+                                    }
+                    },
+        visible:    { comment:      'Shall my markers be visible or not?',
+                      transform:    function(aBoolean){
+                                        if(aBoolean){
+                                            this.get('markerLeft').classList.add('sg-editing-width-marker-left-visible');
+                                            this.get('markerRight').classList.add('sg-editing-width-marker-right-visible');
+                                        }else{
+                                            this.get('markerLeft').classList.remove('sg-editing-width-marker-left-visible');
+                                            this.get('markerRight').classList.remove('sg-editing-width-marker-right-visible');
+                                        }
+                                        return aBoolean;
+                                    }
+                    }
+
+    },
+
+    methods: {
+
+        init: { 
+            comment:    'I initalize myself.',
+            code:       function(editingContainer){
+
+                
+
+                var widthMarkers = (new DOMParser()).parseFromString(this.class.get('markerContainers'), 'text/html').body,
+                    markerLeft   = widthMarkers.childNodes.item(0),
+                    markerRight  = widthMarkers.childNodes.item(1);
+
+                this.set({ 
+                    editingContainer:   editingContainer,
+                    markerLeft:         markerLeft,
+                    markerRight:        markerRight,
+                    visible:            true
+                });
+
+
+                var self = this,
+
+                    onMouseOver = function(evt){
+                        if(!self.get('visible')){
+                            self.get('markerLeft').classList.add('sg-editing-width-marker-left-visible');
+                            self.get('markerRight').classList.add('sg-editing-width-marker-right-visible');
+                        }
+                    },
+                    onMouseOut = function(evt){
+                        if(!self.get('visible')){
+                            self.get('markerLeft').classList.remove('sg-editing-width-marker-left-visible');
+                            self.get('markerRight').classList.remove('sg-editing-width-marker-right-visible');
+                        }
+                    },
+
+                    startX   = 0,
+                    minWidth = 0,
+                    leftSide = false,
+                    wasVisible = true,
+
+                    gridVisible,
+
+                    onMouseDown = function(evt){
+                        if(evt.button !== 0) return;
+                        startX   = evt.pageX;
+                        minWidth = SuperGlue.get('document').do('getMinWidth');
+                        leftSide = evt.target === markerLeft;
+                        SuperGlue.get('document').set({ interactionInProgress: true });
+                        wasVisible = self.get('visible');
+                        self.set({ visible: true });
+
+                        gridVisible = SuperGlue.get('document').get('grid').get('visible');
+                        SuperGlue.get('document').get('grid').set({ visible: true });
+
+                        document.addEventListener('mousemove', onMouseMove, true);
+                        document.addEventListener('mouseup',   onMouseUp,   true);
+
+                        // UNDO
+
+                        evt.stopPropagation();
+                        evt.preventDefault();
+                    },
+                    onMouseUp = function(evt){
+                        document.removeEventListener('mousemove', onMouseMove, true);
+                        document.removeEventListener('mouseup',   onMouseUp,   true);
+                        
+                        var myDocument = SuperGlue.get('document');
+                        myDocument.do('afterLayoutHasChanged');
+                        myDocument.set({ interactionInProgress: false });
+                        
+                        self.set({ visible: wasVisible });
+                        self.get('markerLeft').classList.add('sg-editing-width-marker-left-visible');
+                        self.get('markerRight').classList.add('sg-editing-width-marker-right-visible');
+
+                        SuperGlue.get('document').get('grid').set({ visible: gridVisible });
+
+
+
+                        // UNDO
+                        
+                        evt.stopPropagation();
+                        evt.preventDefault();
+                    },
+                    onMouseMove = function(evt){
+                        var diff       = 2 * (evt.pageX - startX) * (leftSide ? -1 : 1),
+                            myDocument = SuperGlue.get('document'),
+                            oldWidth   = myDocument.get('layout').width,
+                            newWidth   = oldWidth + diff;
+                        if(newWidth > minWidth){
+                            myDocument.set({ layout: {
+                                centered: true,
+                                width:    newWidth
+                            }});
+                            startX = evt.pageX;
+                        }else{
+                            myDocument.set({ layout: {
+                                centered: true,
+                                width:    minWidth
+                            }});
+                        }
+
+                        evt.stopPropagation();
+                        evt.preventDefault();
+                    };
+
+                markerLeft.addEventListener('mouseover', onMouseOver, false);
+                markerRight.addEventListener('mouseover', onMouseOver, false);
+                markerLeft.addEventListener('mouseout', onMouseOut, false);
+                markerRight.addEventListener('mouseout', onMouseOut, false);
+
+                markerLeft.addEventListener('mousedown', onMouseDown, false);
+                markerRight.addEventListener('mousedown', onMouseDown, false);
+
+                window.addEventListener('resize', function(){
+                    self.do('updateHeight');
+                }, false)
+
+            }
+        },
+
+        updateHeight: {
+
+            comment:    'My height must be updated on all relevant changes of page content.',
+            code:       function(){
+
+                var minHeight = SuperGlue.get('document').do('getMinHeight');
+
+                minHeight = minHeight > window.innerHeight ? minHeight : window.innerHeight;
+
+                this.get('markerLeft').style.height = minHeight + 'px';
+                this.get('markerRight').style.height = minHeight + 'px';
+                
+
+            }
+
+        }
+
+
+    }
+
+
+}});

+ 269 - 0
src/allplatforms/classes/Window.js

@@ -0,0 +1,269 @@
+SC.loadPackage({ 'Window': {
+
+    comment: 'I am a window which is managed by the WindowManager and lives on the 2nd level interface.',
+
+
+    sharedProperties: {
+
+        windowMarkup: 	{  	comment:   'This is the basic html markup for all windows.', 
+                        	initValue: '<div class="sg-editing-window">'
+                                            +'<div class="sg-editing-window-titlebar"></div>'
+                                            +'<div class="sg-editing-window-resizehandle"></div>'
+                                            +'<div class="sg-editing-window-closebutton"></div>'
+                                            +'<div class="sg-editing-window-content"></div>'
+                                        +'</div>' 
+                        }
+
+    },
+
+    properties: {
+
+        node:         { comment: 'I hold the DOM node which contains this window.' },
+        titlebar:     { comment: 'I hold the DOM node for the titlebar.' },
+        resizeHandle: { comment: 'I hold the DOM node for the resizeHandle.' },
+        content:      { comment: 'I hold the DOM node for the window\'s content.' },
+
+        top:    { transform: function(anInt){
+                        var val = (
+                            anInt > 0
+                                ? ( anInt + this.get('height') > document.documentElement.clientHeight
+                                        ? document.documentElement.clientHeight - this.get('height')
+                                        : anInt
+                                  )
+                                : 0
+                        );
+                        this.get('node').style.top = val + 'px';
+                        return val;
+                    }
+                },
+        left:   { transform: function(anInt){
+                        var val = (
+                            anInt > 0
+                                ? ( anInt + this.get('width') > document.documentElement.clientWidth
+                                        ? document.documentElement.clientWidth - this.get('width')
+                                        : anInt
+                                  )
+                                : 0
+                        );
+                        this.get('node').style.left = val + 'px';
+                        return val;
+                    }
+                },
+        width:  { transform: function(anInt){
+                        var val = (
+                            anInt >= 250
+                                ? (anInt + this.get('left') > document.documentElement.clientWidth)
+                                    ? document.documentElement.clientWidth - this.get('left')
+                                    : anInt
+                                : 250
+                        );
+                        this.get('node').style.width = val + 'px';
+                        return val;
+                    }
+                },
+        height: { transform: function(anInt){
+                        var val = (
+                            anInt >= 250
+                                ? (anInt + this.get('top') > document.documentElement.clientHeight)
+                                    ? document.documentElement.clientHeight - this.get('top')
+                                    : anInt
+                                : 250
+                        );
+                        this.get('node').style.height = val + 'px';
+                        return val;
+                    }
+                }
+
+    },
+
+    methods: {
+
+    	init: { 
+    		comment: 	'I create a Window according to the windowConfig object: '+
+                        '{ top: anInt, left: anInt, width: anInt, height: anInt }',
+            code: function(windowConfig){
+
+                var self = this,
+                    newWindow = (new DOMParser()).parseFromString(this.class.get('windowMarkup'), 'text/html').body.firstChild;
+
+                this.set({
+                    node:           newWindow,
+                    titlebar:       newWindow.querySelector('.sg-editing-window-titlebar'),
+                    resizeHandle:   newWindow.querySelector('.sg-editing-window-resizehandle'),
+                    content:        newWindow.querySelector('.sg-editing-window-content')
+                })
+
+                var cancelEvent = function(evt){
+                    evt.stopPropagation();
+                }
+                newWindow.addEventListener('mousedown', cancelEvent, false);
+
+                newWindow.querySelector('.sg-editing-window-closebutton').addEventListener('mouseup', function(){
+                    SuperGlue.get('windowManager').do('closeWindow', self);
+                }, false)
+
+                this.do('beDraggable');
+                this.do('beResizable');
+
+                this.set({
+                    top:    windowConfig.top,
+                    left:   windowConfig.left,
+                    width:  windowConfig.width,
+                    height: windowConfig.height
+                });
+
+            }
+        },
+
+        beDraggable: {
+            comment:   'I register the eventListeners on newly created windows to make them draggable.',
+            code:       function(){
+
+                var self = this,
+                    startX,
+                    startY,
+
+                    onMouseDown = function(evt){
+
+                        startX = evt.clientX;
+                        startY = evt.clientY;
+
+                        document.addEventListener('mousemove', onMouseMove, true);
+                        document.addEventListener('mouseup',   onMouseUp,   true);
+
+                        evt.stopPropagation();
+                        evt.preventDefault();
+
+                    },
+
+                    onMouseMove = function(evt){
+
+                        var newX  = self.get('left') - (startX - evt.clientX),
+                            newY  = self.get('top')  - (startY - evt.clientY);
+                        
+                        if(newX > 0){
+                            if(newX + self.get('width') > document.documentElement.clientWidth){
+                                newX = document.documentElement.clientWidth - self.get('width')
+                            }else{
+                                startX = evt.clientX
+                            }
+                        }else{
+                            newX = 0;
+                        }
+
+                        if(newY > 0){
+                            if(newY + self.get('height') > document.documentElement.clientHeight){
+                                newY = document.documentElement.clientHeight - self.get('height')
+                            }else{
+                                startY = evt.clientY
+                            }
+                        }else{
+                            newY = 0;
+                        }
+                        
+
+                        self.set({
+                            top:  newY,
+                            left: newX
+                        })
+
+                        evt.stopPropagation();
+                        evt.preventDefault();
+                        
+                    },
+
+                    onMouseUp = function(evt){
+                        
+                        document.removeEventListener('mousemove', onMouseMove, true);
+                        document.removeEventListener('mouseup',   onMouseUp,   true);
+
+                        evt.stopPropagation();
+                        evt.preventDefault();
+
+                    };
+
+                this.get('titlebar').addEventListener('mousedown', onMouseDown, false);
+
+
+            }
+        },
+
+        beResizable: {
+            comment:   'I register the eventListeners on newly created windows to make them resizable.',
+            code:       function(){
+
+                var self = this,
+                    startX,
+                    startY,
+
+                    onMouseDown = function(evt){
+
+                        startX = evt.clientX;
+                        startY = evt.clientY;
+
+                        document.addEventListener('mousemove', onMouseMove, true);
+                        document.addEventListener('mouseup',   onMouseUp,   true);
+
+                        evt.stopPropagation();
+                        evt.preventDefault();
+
+                    },
+
+                    onMouseMove = function(evt){
+
+                        var newX  = self.get('width')  - (startX - evt.clientX),
+                            newY  = self.get('height') - (startY - evt.clientY);
+                        
+                        if(newX > 250){
+                            if(newX + self.get('left') > document.documentElement.clientWidth){
+                                newX = document.documentElement.clientWidth - self.get('left')
+                            }else{
+                                startX = evt.clientX
+                            }
+                        }else{
+                            newX = 250;
+                        }
+
+                        if(newY > 250){
+                            if(newY + self.get('top') > document.documentElement.clientHeight){
+                                newY = document.documentElement.clientHeight - self.get('top')
+                            }else{
+                                startY = evt.clientY
+                            }
+                        }else{
+                            newY = 250;
+                        }
+                        
+
+                        self.set({
+                            height: newY,
+                            width:  newX
+                        })
+
+                        evt.stopPropagation();
+                        evt.preventDefault();
+                        
+                    },
+
+                    onMouseUp = function(evt){
+                        
+                        document.removeEventListener('mousemove', onMouseMove, true);
+                        document.removeEventListener('mouseup',   onMouseUp,   true);
+
+                        evt.stopPropagation();
+                        evt.preventDefault();
+
+                    };
+
+                this.get('resizeHandle').addEventListener('mousedown', onMouseDown, false);
+
+
+            }
+        }
+
+
+
+    }
+
+
+}});

+ 95 - 0
src/allplatforms/classes/WindowManager.js

@@ -0,0 +1,95 @@
+SC.loadPackage({ 'WindowManager': {
+
+    comment: 'I am the WindowManager which organizes the editing interface in the 2nd level.',
+
+
+    properties: {
+
+        windowsContainer:   { comment: 'I hold the DOM node which contains the windows.' },
+        windows:            { comment: 'I hold an map of id-->references to my window DOM nodes.' }
+
+    },
+
+    methods: {
+
+    	init: { 
+    		comment: 	'I init the WindowManager.',
+    		code: 		function(){
+
+                var self = this;
+
+                this.set({  
+                    
+                    windows:            [],
+
+                    windowsContainer:   (function(){
+                                            var windowsContainer = document.createElement('div');
+                                            windowsContainer.setAttribute('id', 'sg-editing-windows-container');
+                                            document.body.appendChild(windowsContainer);
+                                            return windowsContainer;
+                                        }).call(this)
+                });
+
+                window.addEventListener('resize', function(){
+                    var windows = self.get('windows'); 
+                    for(var i in windows){
+                        windows[i].set({
+                            top:    windows[i].get('top'),
+                            left:   windows[i].get('left'),
+                            width:  windows[i].get('width'),
+                            height: windows[i].get('height')
+                        });
+                    }
+                }, false)
+
+
+    		}
+    	},
+
+        createWindow: {
+            comment:    'I create a Window according to the windowConfig object: '+
+                        '{ class: aWindowClass, top: anInt, left: anInt, width: anInt, height: anInt }',
+            code: function(windowConfig){
+
+                var self      = this,
+                    newWindow = SC.init(windowConfig.class, windowConfig),
+
+                    windowsContainer = this.get('windowsContainer');
+
+
+                newWindow.get('node').addEventListener('mousedown', function(evt){
+                    if(windowsContainer.lastElementChild !== newWindow.get('node')){
+                        windowsContainer.appendChild(newWindow.get('node'));
+                    }
+                }, false);
+
+
+                windowsContainer.appendChild(newWindow.get('node'));
+
+                this.get('windows').push(newWindow);
+
+                return newWindow;
+
+            }
+        },
+
+        closeWindow: {
+            comment: 'I close a given window',
+            code: function(aWindow){
+
+                var windows = this.get('windows');
+                windows.splice(windows.indexOf(aWindow), 1);
+
+                this.get('windowsContainer').removeChild(aWindow.get('node'));
+
+
+
+            }
+        }
+
+
+
+    }
+
+
+}});

+ 33 - 0
src/allplatforms/classes/_ClassTemplate.js

@@ -0,0 +1,33 @@
+SC.loadPackage({ 'myClass': {
+
+    comment: 'my class comment.',
+
+
+    sharedProperties: {
+
+        key: 	{  	comment:   'class property comment', 
+                	initValue: '1' 
+                }
+
+    },
+
+    properties: {
+
+        key: { comment: 'instance property comment' }
+
+    },
+
+    methods: {
+
+    	key: { 
+    		comment: 	'method comment',
+    		code: 		function(){
+
+    		}
+    	}
+
+
+    }
+
+
+}});

+ 16 - 0
src/allplatforms/css/ColorPickerWidget.css

@@ -0,0 +1,16 @@
+
+.sg-editing-widget-colorPicker-panel {
+	position: 			absolute;
+	top: 				30px;
+	left: 				0px;
+}
+
+
+
+
+
+.sg-editing-widget-panel > .sg-widget-triangle-up {
+	top:  		-15px;;
+	left: 		0px;
+}
+

+ 110 - 0
src/allplatforms/css/CreationMenu.css

@@ -0,0 +1,110 @@
+
+#sg-editing-creation-menu {
+	pointer-events: all;
+	position:       absolute;
+	z-index:		200;
+
+	background:  	url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAGCAYAAADgzO9IAAAAGklEQVQImWP4r237/7+27X8GdIBTgihAmbEAtfATMUvnCp0AAAAASUVORK5CYII=") repeat;	
+
+}
+
+#sg-editing-creation-menu-items  {
+	position: 	absolute;
+	width: 		140px;
+	height:		50px;
+}
+
+#sg-editing-creation-menu-items.sg-editing-creation-menu-nw  {
+	top: 	-45px;
+	left: 	0px;
+}
+
+#sg-editing-creation-menu-items.sg-editing-creation-menu-ne  {
+	top: 	-45px;
+	right: 	0px;
+}
+
+#sg-editing-creation-menu-items.sg-editing-creation-menu-sw  {
+	bottom:	-45px;
+	left: 	0px;
+}
+
+#sg-editing-creation-menu-items.sg-editing-creation-menu-se  {
+	bottom:	-45px;
+	right:	0px;
+}
+
+
+
+
+/* Items */
+
+.sg-editing-creation-menu-container {
+	pointer-events: 	all;
+
+	width: 				30px;
+	height: 			30px;
+	margin: 			0px;
+	font-family: 		Dosis, sans-serif;
+	letter-spacing: 	1px;
+}
+
+.sg-editing-creation-menu-button {
+	position: 			relative;
+	cursor: 			pointer;
+	width: 				30px;
+	height: 			30px;
+	margin: 			0;
+	padding: 			1px 6px; /* icon files are 18x28 */
+	border: 			none;
+	outline: 			none;
+	overflow: 			visible;
+
+	background-color: 		rgb(255, 41, 61);
+	background-repeat: 		no-repeat;
+	background-size: 		30px auto;
+	background-position:	center;
+	-webkit-appearance: 	button;
+}
+
+
+
+.sg-editing-creation-menu-nw > .sg-editing-creation-menu-container {
+	float: 			left;
+	margin-right:	5px;
+	margin-bottom:	20px;
+}
+.sg-editing-creation-menu-ne > .sg-editing-creation-menu-container {
+	float: 			right;
+	margin-left:	5px;
+	margin-bottom:	20px;
+}
+.sg-editing-creation-menu-sw > .sg-editing-creation-menu-container {
+	float: 			left;
+	margin-right:	5px;
+	margin-top:		20px;
+}
+.sg-editing-creation-menu-se > .sg-editing-creation-menu-container {
+	float: 			right;
+	margin-left:	5px;
+	margin-top:		20px;
+}
+
+
+/* Menu Button Icons */
+
+#sg-editing-creation-menu-embedElement {
+	background-image: url("icons/createEmbedElement.svg");
+}
+
+#sg-editing-creation-menu-iframeElement {
+	background-image: url("icons/createIFrameElement.svg");
+}
+
+#sg-editing-creation-menu-imageElement {
+	background-image: url("icons/createImageElement.svg");
+}
+
+#sg-editing-creation-menu-textElement {
+	background-image: url("icons/createTextElement.svg");
+}

+ 27 - 0
src/allplatforms/css/Document.css

@@ -0,0 +1,27 @@
+
+/* Changes to user content */
+
+.sg-editing-outlines > .sg-element {
+   outline: 1px dashed rgb(255, 41, 61);
+}
+
+
+.sg-editing-block-events {
+	pointer-events: 		all;
+}
+.sg-editing-block-events > * {
+	pointer-events: 		none;
+}
+
+
+/* Editing Container */
+
+#sg-editing-container {
+	pointer-events: 	none;
+	position: 			relative;
+	top: 				0px;
+}
+#sg-editing-container.sg-editing-container-centered {
+	margin: 0px auto;
+}
+

+ 17 - 0
src/allplatforms/css/DocumentMenu.css

@@ -0,0 +1,17 @@
+
+#sg-editing-document-menu {
+	pointer-events: none;
+	position:       relative;
+	top:            0px;
+}
+
+#sg-editing-document-menu-top  {
+	position: 		absolute;
+	width:			300px;
+	height:			30px;
+}
+
+#sg-editing-document-menu-left {
+	position: 		absolute;
+	width:			30px;
+}

+ 389 - 0
src/allplatforms/css/FileManagerWindow.css

@@ -0,0 +1,389 @@
+.sg-filemanager-working {
+	position: 			absolute;
+	top: 				30px;
+	right: 				15px;
+	width: 				30px;
+	height: 			30px;
+	background-image:	url(./icons/loading.gif);
+	background-repeat: 	no-repeat;
+	background-size:	28px auto;
+	background-position: center;
+}
+
+.sg-filemanager-operations {
+	position:			absolute;
+	bottom:				15px;
+	left:				15px;
+	height: 			30px;
+	margin-right: 		15px;
+	z-index:			1;
+}
+
+.sg-filemanager-operation {
+	width: 				30px;
+	height: 			30px;
+	background-color: 	rgb(255, 41, 61);
+	background-repeat: 	no-repeat;
+	background-size: 	auto 26px;
+	background-position: center;
+	opacity:			.6;
+}
+
+.sg-filemanager-operation.active {
+	opacity:			1;
+}
+
+.sg-filemanager-operations .sg-filemanager-operation {
+	float: 				left;
+}
+
+.sg-filemanager-operation.active:hover {
+	background-color:	rgb(183, 12, 29);
+	cursor:				pointer;
+}
+
+.sg-filemanager-operations .sg-filemanager-operation.file-upload {
+	background-image:	url(./icons/uploadFile.png);
+}
+
+.sg-filemanager-operations .sg-filemanager-operation.directory-new {
+	background-image:	url(./icons/newDirectory.png);
+}
+
+.sg-filemanager-operations .sg-filemanager-operation.rename {
+	background-image:	url(./icons/copy.svg);
+}
+
+.sg-filemanager-operations .sg-filemanager-operation.copy {
+	background-image:	url(./icons/copy.svg);
+}
+
+.sg-filemanager-operations .sg-filemanager-operation.paste {
+	background-image:	url(./icons/uploadFile.png);
+}
+
+.sg-filemanager-operations .sg-filemanager-operation.delete {
+	background-image:	url(./icons/delete.svg);
+}
+
+.sg-filemanager-directory-container {
+	position: 			relative;
+	width:				100%;
+	height:				100%;
+	border-style:		solid;
+	border-color:		rgb(255, 41, 61);
+	border-width:		45px 0 45px 0;
+	-moz-box-sizing:	border-box;
+	-webkit-box-sizing:	border-box;
+	box-sizing:			border-box;
+}
+
+.sg-filemanager-directory-container.nameInput {
+	border-width:		45px 0 75px 0;
+}
+
+.sg-filemanager-directory-container.nameInput .sg-modal-name-input {
+	position: 			absolute;
+	bottom: 			-37px;
+	left: 				0px;
+	width: 				100%;
+	font-size:			14px;
+	font-family: 		Dosis, sans-serif;
+	letter-spacing: 	1px;
+	padding: 			1px 3px;
+	-moz-box-sizing:	border-box;
+	-webkit-box-sizing:	border-box;
+	box-sizing:			border-box;
+}
+
+.sg-filemanager-directory-container .sg-filemanager-directory-listing {
+	width:				100%;
+	height:				100%;
+	margin: 			0;
+	padding: 			0;
+	overflow-x: 		hidden;
+	overflow-y: 		auto;
+}
+
+.sg-filemanager-directory-container .sg-filemanager-blocked {
+	position:			absolute;
+	top:				0;
+	left:				0;
+	width:				100%;
+	height:				100%;
+	background-color:	rgba(255, 41, 61, .6);
+	z-index:			3;
+	cursor: 			default;
+}
+
+.sg-filemanager-directory-container .sg-filemanager-directory-listing li {
+	position: 			relative;
+	color:				#fff;
+	padding:			6px 6px 6px 31px;
+	background-repeat:	no-repeat;
+	background-size:	auto 20px;
+	background-position: 4px center;
+	opacity: 			.88;
+}
+
+.sg-filemanager-directory-container .sg-filemanager-directory-listing li span.size {
+	position:			absolute;
+	right: 				0px;
+	top: 				0px;
+	height: 			100%;
+	width:				62px;
+	background-color:	rgb(255, 41, 61);
+	padding:			6px;
+	text-align:			right;
+	color:				#ffbfcf;
+	-moz-box-sizing:	border-box;
+	-webkit-box-sizing:	border-box;
+	box-sizing:			border-box;
+	opacity: 			0;
+}
+
+.sg-filemanager-directory-container .sg-filemanager-directory-listing li:after {
+	content:			'';
+	position:			absolute;
+	top:				0;
+	right: 				0px;
+	width: 				20px;
+	height: 			100%;
+	background: 		moz-linear-gradient(left,  rgba(255,255,255,0) 0%, rgba(255,41,61,0.99) 99%, rgba(255,41,61,1) 100%);
+	background: 		webkit-gradient(linear, left top, right top, color-stop(0%,rgba(255,255,255,0)), color-stop(99%,rgba(255,41,61,0.99)), color-stop(100%,rgba(255,41,61,1)));
+	background: 		webkit-linear-gradient(left,  rgba(255,255,255,0) 0%,rgba(255,41,61,0.99) 99%,rgba(255,41,61,1) 100%);
+	background:			linear-gradient(to right,  rgba(255,255,255,0) 0%,rgba(255,41,61,0.99) 99%,rgba(255,41,61,1) 100%);
+}
+
+.sg-filemanager-directory-container .sg-filemanager-directory-listing li:hover, .sg-filemanager-directory-container .sg-filemanager-directory-listing li.active {
+	background-color:	rgb(183, 12, 29);
+	cursor:				pointer;
+}
+
+.sg-filemanager-directory-container .sg-filemanager-directory-listing li.active {
+	opacity: 			1;
+}
+
+.sg-filemanager-directory-container .sg-filemanager-directory-listing li:hover:after, .sg-filemanager-directory-container .sg-filemanager-directory-listing li.active:after, .sg-filemanager-directory-container .sg-filemanager-directory-listing li.new:after {
+	display:			none;
+}
+
+.sg-filemanager-directory-container .sg-filemanager-directory-listing li[data-type="file"]:hover:after, .sg-filemanager-directory-container .sg-filemanager-directory-listing li[data-type="file"].active:after {
+	content:			'';
+	position:			absolute;
+	top:				0;
+	right: 				56px;
+	width: 				20px;
+	height: 			100%;
+	background: 		moz-linear-gradient(left,  rgba(255,255,255,0) 0%, rgba(183, 12, 29, 99) 99%, rgba(255,41,61,1) 100%);
+	background: 		webkit-gradient(linear, left top, right top, color-stop(0%,rgba(255,255,255,0)), color-stop(99%,rgba(183, 12, 29, 99)), color-stop(100%,rgba(255,41,61,1)));
+	background: 		webkit-linear-gradient(left,  rgba(255,255,255,0) 0%,rgba(183, 12, 29, 99) 99%,rgba(255,41,61,1) 100%);
+	background:			linear-gradient(to right,  rgba(255,255,255,0) 0%,rgba(183, 12, 29, 99) 99%,rgba(255,41,61,1) 100%);
+	display: 			block !important;
+}
+
+.sg-filemanager-directory-container .sg-filemanager-directory-listing li:hover span.size, .sg-filemanager-directory-container .sg-filemanager-directory-listing li.active span.size {
+	opacity: 			1;
+	-moz-transition-property: opacity;
+	-moz-transition-duration: 300ms;
+	-moz-transition-delay: 400ms;
+	-webkit-transition-property: opacity;
+	-webkit-transition-duration: 300ms;
+	-webkit-transition-delay: 400ms;
+	transition-property: opacity;
+	transition-duration: 300ms;
+	transition-delay: 400ms;
+	background-color:	rgb(183, 12, 29);
+}
+
+.sg-filemanager-directory-container .sg-filemanager-directory-listing li.edit:after, .sg-filemanager-directory-container .sg-filemanager-directory-listing li.active.edit:after, .sg-filemanager-directory-container .sg-filemanager-directory-listing li.edit:hover:after, .sg-filemanager-directory-container .sg-filemanager-directory-listing li.edit span.size {
+	display: 			none !important;
+}
+
+.sg-filemanager-directory-container .sg-filemanager-directory-listing li.sg-resource-directory {
+	background-image:	url(./icons/resourceDirectory.png);
+}
+
+.sg-filemanager-directory-container .sg-filemanager-directory-listing li.sg-resource-file {
+	background-image:	url(./icons/resourceFile.png);
+}
+
+.sg-filemanager-directory-container .sg-filemanager-directory-listing li.new, .sg-filemanager-directory-container .sg-filemanager-directory-listing li.edit {
+	background-color:	rgb(183, 12, 29);
+}
+
+.sg-filemanager-directory-container .sg-filemanager-directory-listing li input[type="text"] {
+	font-family: 		Dosis, sans-serif;
+	letter-spacing: 	1px;
+	padding: 			1px 3px;
+	position: 			absolute;
+	top: 				4px;
+	left: 				26px;
+	width: 				80%;
+	font-size: 			14px;
+	height: 			17px;
+}
+
+.sg-filemanager-directory-controls {
+	position:			absolute;
+	top: 				30px;
+	left:				0;
+	width: 				100%;
+	height: 			30px;
+	border-style:		solid;
+	border-color:		rgb(255, 41, 61);
+	border-width:		0px 15px 0px 15px;
+	-moz-box-sizing:	border-box;
+	-webkit-box-sizing:	border-box;
+	box-sizing:			border-box;
+	overflow:			hidden;
+}
+
+.sg-filemanager-directory-controls .sg-filemanager-operation.directory-up {
+	position:			absolute;
+	top:				0;
+	left:				0;
+	z-index:			1;
+	background-image:	url(./icons/levelUp.png);
+}
+
+.sg-filemanager-directory-controls .sg-filemanager-current-path {
+	width:				100%;
+	height:				30px;
+	color:				#fff;
+	line-height:		30px;
+	border-style:		solid;
+	border-color:		rgb(255, 41, 61);
+	border-width:		0px 0px 0px 40px;
+	white-space: 		nowrap;
+	-moz-box-sizing:	border-box;
+	-webkit-box-sizing:	border-box;
+	box-sizing:			border-box;
+}
+
+.sg-filemanager-directory-controls .sg-filemanager-current-path:after {
+	content:			'';
+	position:			absolute;
+	top:				0;
+	right: 				0;
+	width: 				15px;
+	height: 			100%;
+	background: 		moz-linear-gradient(left,  rgba(255,255,255,0) 0%, rgba(255,41,61,0.99) 99%, rgba(255,41,61,1) 100%);
+	background: 		webkit-gradient(linear, left top, right top, color-stop(0%,rgba(255,255,255,0)), color-stop(99%,rgba(255,41,61,0.99)), color-stop(100%,rgba(255,41,61,1)));
+	background: 		webkit-linear-gradient(left,  rgba(255,255,255,0) 0%,rgba(255,41,61,0.99) 99%,rgba(255,41,61,1) 100%);
+	background:			linear-gradient(to right,  rgba(255,255,255,0) 0%,rgba(255,41,61,0.99) 99%,rgba(255,41,61,1) 100%);
+}
+
+.sg-filemanager-preview {
+	position: 			absolute;
+	bottom: 			15px;
+	right: 				15px;
+	width: 				60px;
+	height:				37px;
+	padding: 			3px;
+	background-color:	transparent;
+	overflow: 			hidden;
+	-moz-transition-property: width, height;
+	-moz-transition-duration: 600ms;
+	-moz-transition-delay: 600ms;
+	-webkit-transition-property: width, height;
+	-webkit-transition-duration: 600ms;
+	-webkit-transition-delay: 600ms;
+	transition-property: width, height;
+	transition-duration: 600ms;
+	transition-delay: 600ms;
+}
+
+.sg-filemanager-preview.active {
+	background-color:	#ef9999;
+	border:				2px solid rgb(183, 12, 29);
+}
+
+.sg-filemanager-preview img {
+	display: 			block;
+	margin: 			0 auto;
+	max-width: 			100%;
+	max-height: 		100%;
+}
+
+.sg-filemanager-preview.active:hover {
+	width: 				100px;
+	height:				62px;
+}
+
+.sg-filemanager-progress-bar {
+	position: 			absolute;
+	bottom:				0;
+	left: 				0;
+	width: 				100%;
+	height: 			7.5px;
+	z-index: 			3;
+}
+
+.sg-filemanager-progress-bar .sg-filemanager-progress {
+	position:			absolute;
+	top:				0;
+	left:				0;
+	width: 				0px;
+	height: 			100%;
+	background-color:	rgb(183, 12, 29);
+	-moz-transition-property: all;
+	-moz-transition-duration: 800ms;
+	-webkit-transition-property: all;
+	-webkit-transition-duration: 800ms;
+	transition-property: all;
+	transition-duration: 800ms;
+}
+
+.sg-editing-filemanager-modal-container {
+	position: 			absolute;
+	bottom: 			-38px;
+	right: 				0px;
+	width: 				60px;
+	height: 			30px;
+	padding:			4px;
+	background-color: 	rgb(255, 41, 61);
+}
+
+.sg-editing-filemanager-modal-container:before {
+	content:			'';
+	position:			absolute;
+	z-index: 			-1;
+	top: 				0;
+	left: 				0;
+	width:				68px;
+	height:				38px;
+	-moz-box-shadow:	0 0 2px 3px #555;
+	-webkit-box-shadow:	0 0 2px 3px #555;
+	box-shadow:			0 0 2px 3px #555;
+}
+
+.sg-editing-filemanager-modal-container button {
+	display:			inline-block;
+	width: 				30px;
+	height: 			30px;
+	border:				none;
+	outline:			none;
+	background-color: 	rgb(255, 41, 61);
+	background-repeat: 	no-repeat;
+	background-size: 	30px auto;
+	background-position: center;
+	opacity: 			.6;
+}
+
+.sg-editing-filemanager-modal-container button.active {
+	opacity:			1;
+}
+
+.sg-editing-filemanager-modal-container button.active:hover {
+	background-color: 	rgb(183, 12, 29);
+	cursor:				pointer;
+}
+
+.sg-editing-filemanager-modal-container button.confirm {
+	background-image:	url(./icons/copy.svg);
+}
+
+.sg-editing-filemanager-modal-container button.cancel {
+	background-image:	url(./icons/copy.svg);
+}

+ 36 - 0
src/allplatforms/css/Grid.css

@@ -0,0 +1,36 @@
+
+#sg-editing-grid-container {
+
+	pointer-events: 	none;
+	position: 			absolute;
+	top: 				0px;
+	background-repeat:  repeat;	
+
+}
+
+#sg-editing-grid-control {
+	pointer-events: all;
+	cursor: 		col-resize;
+
+	position: 		absolute;
+	z-index: 		300;
+	top:			0px;
+
+	width: 			0px;
+	height: 		0px;
+	border-style: 	solid;
+	border-width: 	20px 20px 0px 20px;
+	border-color: 	#ff2b40 transparent transparent transparent;
+}
+
+#sg-editing-grid-control.sg-editing-grid-control-hidden {
+
+	opacity: 0;
+
+}
+
+#sg-editing-grid-control.sg-editing-grid-control-hidden:hover {
+
+	opacity: 1;
+
+}

+ 62 - 0
src/allplatforms/css/HTMLEditor.css

@@ -0,0 +1,62 @@
+.sg-editing-superuser-textarea {
+	width: 				100%;
+	height: 			100%;
+	border: 			none;
+	padding: 			15px;
+	-moz-box-sizing:	border-box;
+	-webkit-box-sizing:	border-box;
+	box-sizing:			border-box;
+	resize: 			none;
+	background-color: 	#fff;
+	font-family: 		'Courier New', Arial;
+	color:				#000000;
+	font-size:			13px;
+}
+
+.sg-editing-superuser-modal-container {
+	position: 			absolute;
+	bottom: 			-38px;
+	right: 				0px;
+	width: 				60px;
+	height: 			30px;
+	padding:			4px;
+	background-color: 	rgb(255, 41, 61);
+}
+
+.sg-editing-superuser-modal-container:before {
+	content:			'';
+	position:			absolute;
+	z-index: 			-1;
+	top: 				0;
+	left: 				0;
+	width:				68px;
+	height:				38px;
+	-moz-box-shadow:	0 0 2px 3px #555;
+	-webkit-box-shadow:	0 0 2px 3px #555;
+	box-shadow:			0 0 2px 3px #555;
+}
+
+.sg-editing-superuser-modal-container button {
+	display:			inline-block;
+	width: 				30px;
+	height: 			30px;
+	border:				none;
+	outline:			none;
+	background-color: 	rgb(255, 41, 61);
+	background-repeat: 	no-repeat;
+	background-size: 	30px auto;
+	background-position: center;
+}
+
+.sg-editing-superuser-modal-container button:hover {
+	background-color: 	rgb(183, 12, 29);
+	cursor:				pointer;
+}
+
+.sg-editing-superuser-modal-container button.confirm {
+	background-image:	url(./icons/copy.svg);
+}
+
+.sg-editing-superuser-modal-container button.cancel {
+	background-image:	url(./icons/copy.svg);
+}

+ 91 - 0
src/allplatforms/css/MenuItem.css

@@ -0,0 +1,91 @@
+
+.sg-editing-menu-container {
+	pointer-events: 	all;
+	cursor: 			default !important;
+	position: 			relative;
+	width: 				30px;
+	height: 			30px;
+	margin: 			0px;
+	font-family: 		Dosis, sans-serif;
+	letter-spacing: 	1px;
+
+}
+
+.sg-editing-menu-button {
+	position: 			relative;
+	cursor: 			pointer;
+	width: 				30px;
+	height: 			30px;
+	margin: 			0;
+	padding: 			1px 6px;
+	border: 			none;
+	outline: 			none;
+	overflow: 			visible;
+
+	background-color: 		rgb(255, 41, 61);
+	background-repeat: 		no-repeat;
+	background-size: 		30px auto;
+	background-position:	center;
+	-webkit-appearance: 	button;
+}
+
+.sg-editing-menu-container.active > .sg-editing-menu-button {
+	background-color: 		rgb(183, 12, 29);
+}
+
+.sg-editing-menu-button::-moz-focus-inner {
+	border: 0;
+}
+
+#sg-editing-document-menu-top > .sg-editing-menu-container {
+	float: 				left;
+	margin-right: 		5px;
+}
+
+#sg-editing-document-menu-left > .sg-editing-menu-container {
+	margin-bottom: 		5px;
+}
+
+
+
+.sg-editing-menu-panel {
+	position: 			absolute;
+	top: 				0px;
+	left: 				15px;
+	width: 				100%;
+	height: 			34px;
+	background-color: 	rgb(183, 12, 29);
+}
+
+.sg-editing-menu-panel .sg-editing-menu-button {
+	position: 				absolute;
+	background-color: 		rgb(183, 12, 29);
+}
+
+
+
+.sg-menu-triangle-top {
+	position: 		absolute;
+	width: 			0;
+	height: 		0;
+	border-style: 	solid;
+	border-width: 	15px 15px 0 15px;
+	border-color: 	rgb(183, 12, 29) transparent transparent transparent;
+}
+
+.sg-menu-triangle-right {
+	position: 		absolute;
+	width: 			0;
+	height: 		0;
+	border-style: 	solid;
+	border-width: 	15px 0 15px 15px;
+	border-color: 	transparent transparent transparent rgb(183, 12, 29);
+}
+
+.sg-editing-menu-container input[type="text"] {
+	position: 		absolute;
+	border:			none;
+	margin: 		0px;
+	padding:		4px;
+	height: 		22px;
+}

+ 18 - 0
src/allplatforms/css/MenuItemBackgroundColor.css

@@ -0,0 +1,18 @@
+#sg-editing-menu-backgroundColor {
+	background-image: url("icons/backgroundColor.svg");
+}
+
+
+.sg-editing-menu-colorPicker-panel {
+	position: 			absolute;
+	top: 				0px;
+	left: 				30px;
+}
+
+
+
+.sg-editing-menu-panel > .sg-widget-triangle-right {
+	top:  		0px;;
+	left: 		0px;
+}
+

+ 45 - 0
src/allplatforms/css/MenuItemBackgroundImg.css

@@ -0,0 +1,45 @@
+#sg-editing-menu-backgroundImg {
+	background-image: url("icons/backgroundImg.svg");;
+}
+
+#sg-editing-menu-backgroundImg-panel {
+	position: 			absolute;
+	top: 				0px;
+	left: 				30px;
+	width: 				246px;
+	height: 			32px;
+}
+
+
+
+
+.sg-editing-menu-panel > .sg-menu-triangle-right {
+	top:  		0px;;
+	left: 		-15px;
+}
+
+#sg-editing-menu-backgroundImg-input {
+	position: absolute;
+	top:		2px;
+	left:		2px;
+	width:		170px;
+
+}
+
+#sg-editing-menu-backgroundImg-chooseFile {
+	top:		2px;
+	left:		183px;
+}
+
+#sg-editing-menu-backgroundImg-clear {
+	top:		2px;
+	left:		214px;
+}
+
+#sg-editing-menu-backgroundImg-chooseFile {
+	background-image: url("icons/fileManager.svg");
+}
+
+#sg-editing-menu-backgroundImg-clear {
+	background-image: url("icons/clear.svg");
+}

+ 50 - 0
src/allplatforms/css/MenuItemBackgroundRepeat.css

@@ -0,0 +1,50 @@
+#sg-editing-menu-backgroundRepeat {
+	background-image: url("icons/backgroundRepeat.svg");;
+}
+
+
+
+#sg-editing-menu-backgroundRepeat-panel {
+	position: 			absolute;
+	top: 				0px;
+	left: 				30px;
+	width: 				98px;
+	height: 			32px;
+}
+
+
+
+
+.sg-editing-menu-panel > .sg-menu-triangle-right {
+	top:  		0px;;
+	left: 		-15px;
+}
+
+
+
+#sg-editing-menu-backgroundRepeat-panel .sg-editing-menu-button {
+	background-color: rgb(255, 41, 61);
+}
+
+#sg-editing-menu-backgroundRepeat-panel .sg-editing-menu-button:hover {
+	background-color: rgb(183, 12, 29);
+}
+
+
+#sg-editing-menu-backgroundRepeat-tile {
+	top: 	2px;
+	left: 	2px;
+	background-image: url("icons/tile.svg");
+}
+
+#sg-editing-menu-backgroundRepeat-tileX {
+	top:	2px;
+	left: 	34px;
+	background-image: url("icons/tileX.svg");
+}
+
+#sg-editing-menu-backgroundRepeat-tileY {
+	top:	2px;
+	left: 	66px;
+	background-image: url("icons/tileY.svg");
+}

+ 3 - 0
src/allplatforms/css/MenuItemCenter.css

@@ -0,0 +1,3 @@
+#sg-editing-menu-center {
+	background-image: url("icons/center.png");
+}

+ 3 - 0
src/allplatforms/css/MenuItemFileManager.css

@@ -0,0 +1,3 @@
+#sg-editing-menu-fileManager {
+	background-image: url("icons/fileManager.svg");;
+}

+ 3 - 0
src/allplatforms/css/MenuItemOutlines.css

@@ -0,0 +1,3 @@
+#sg-editing-menu-outlines {
+	background-image: url("icons/outlines.svg");
+}

+ 37 - 0
src/allplatforms/css/MenuItemPageTitle.css

@@ -0,0 +1,37 @@
+#sg-editing-menu-pageTitle {
+	background-image: url("icons/pageTitle.svg");;
+}
+
+
+#sg-editing-menu-pageTitle-panel {
+	position: 			absolute;
+	top: 				0px;
+	left: 				30px;
+	width: 				216px;
+	height: 			32px;
+}
+
+
+
+
+.sg-editing-menu-panel > .sg-menu-triangle-right {
+	top:  		0px;;
+	left: 		-15px;
+}
+
+#sg-editing-menu-pageTitle-input {
+	position: absolute;
+	top:		2px;
+	left:		2px;
+	width:		170px;
+
+}
+
+#sg-editing-menu-pageTitle-clear {
+	top:		2px;
+	left:		183px;
+}
+
+#sg-editing-menu-pageTitle-clear {
+	background-image: url("icons/clear.svg");
+}

+ 3 - 0
src/allplatforms/css/MenuItemPaste.css

@@ -0,0 +1,3 @@
+#sg-editing-menu-paste {
+	background-image: url("icons/paste.svg");;
+}

+ 3 - 0
src/allplatforms/css/MenuItemRedo.css

@@ -0,0 +1,3 @@
+#sg-editing-menu-redo {
+	background-image: url("icons/redo.svg");;
+}

+ 3 - 0
src/allplatforms/css/MenuItemSave.css

@@ -0,0 +1,3 @@
+#sg-editing-menu-save {
+	background-image: url("icons/save.svg");;
+}

+ 3 - 0
src/allplatforms/css/MenuItemSaveAs.css

@@ -0,0 +1,3 @@
+#sg-editing-menu-saveAs {
+	background-image: url("icons/saveAs.svg");;
+}

+ 40 - 0
src/allplatforms/css/MenuItemSaveRemote.css

@@ -0,0 +1,40 @@
+#sg-editing-menu-saveRemote {
+	background-image: url("icons/saveRemote.svg");;
+}
+
+
+#sg-editing-menu-saveRemote-panel {
+	position: 			absolute;
+	top: 				30px;
+	left: 				0px;
+	width: 				216px;
+	height: 			32px;
+}
+
+#sg-editing-menu-saveRemote-panel .sg-editing-menu-panel {
+	top: 				15px;
+	left: 				0px;
+}
+
+
+.sg-editing-menu-panel > .sg-menu-triangle-top {
+	top:  		-15px;;
+	left: 		0px;
+}
+
+#sg-editing-menu-saveRemote-input {
+	position: absolute;
+	top:		2px;
+	left:		2px;
+	width:		170px;
+
+}
+
+#sg-editing-menu-saveRemote-confirm {
+	top:		2px;
+	left:		183px;
+}
+
+#sg-editing-menu-saveRemote-confirm {
+	background-image: url("icons/confirm.svg");
+}

+ 3 - 0
src/allplatforms/css/MenuItemUndo.css

@@ -0,0 +1,3 @@
+#sg-editing-menu-undo {
+	background-image: url("icons/undo.svg");;
+}

+ 83 - 0
src/allplatforms/css/ResizeHandles.css

@@ -0,0 +1,83 @@
+
+.sg-editing-element-no-outline {
+	outline: none !important;
+}
+
+.sg-editing-resize-handles {
+	position: absolute;
+	pointer-events: none;
+}
+
+.sg-editing-resize-handles:before {
+	/* Firefox workaround css outline behavior */
+	content: '';
+	position: absolute; 
+	margin: 0px;
+	padding: 0px;
+	top: 0px;
+	left: 0px;
+	right: 0px;
+	bottom: 0px;
+	outline: 1px dashed rgb(255, 41, 61);
+}
+
+.sg-editing-resize-handle {
+	position: absolute;
+	pointer-events: all;
+	width: 10px;
+	height: 10px;
+	background-color: rgb(255, 41, 61);
+	border: none;
+}
+
+.sg-editing-resize-handle.nwgrip {
+	cursor: nw-resize;
+	top:-1px;
+	left:-1px;
+}
+
+.sg-editing-resize-handle.negrip {
+	cursor: ne-resize;
+	top:-1px;
+	right:-1px;
+}
+
+.sg-editing-resize-handle.swgrip {
+	cursor: sw-resize;
+	bottom:-1px;
+	left:-1px;
+}
+
+.sg-editing-resize-handle.segrip {
+	cursor: se-resize;
+	bottom:-1px;
+	right:-1px;
+}
+
+.sg-editing-resize-handle.ngrip {
+	cursor: n-resize;
+	top:-1px; 
+	left:50%;
+	margin-left:-5px;
+}
+
+.sg-editing-resize-handle.egrip {
+	cursor: e-resize;
+	right:-1px; 
+	top:50%;
+	margin-top:-5px;
+}
+
+.sg-editing-resize-handle.sgrip {
+	cursor: s-resize;
+	bottom:-1px; 
+	left:50%;
+	margin-left:-5px;
+}
+
+.sg-editing-resize-handle.wgrip {
+	cursor: w-resize;
+	left:-1px; 
+	top:50%;
+	margin-top:-5px;
+}

+ 36 - 0
src/allplatforms/css/Selection.css

@@ -0,0 +1,36 @@
+
+.sg-editing-selection {
+	position: absolute;
+	pointer-events: none;
+}
+
+.sg-editing-selection-outline:before {
+	/* Firefox workaround css outline behavior */
+	content:  '';
+	position: absolute;
+	z-index:  100;
+	margin:   0px;
+	padding:  0px;
+	top:      0px;
+	left:     0px;
+	right:    0px;
+	bottom:   0px;
+	outline:  2px dashed rgb(255, 41, 61);
+}
+
+.sg-editing-selection-widget-menu-right {
+	position: absolute;
+	z-index:  100;
+	right:    -45px;
+	bottom:   0px;
+	width:    30px;
+}
+
+.sg-editing-selection-widget-menu-bottom {
+	position: absolute;
+	z-index:  100;
+	right:    0px;
+	bottom:   -45px;
+	height:   30px;
+	width:    500px;
+}

+ 44 - 0
src/allplatforms/css/SliderWidget.css

@@ -0,0 +1,44 @@
+
+
+
+
+.sg-editing-widget-slider-panel {
+	position: 			absolute;
+	top: 				30px;
+	left: 				0px;
+	width: 				30px;
+	height: 			120px;
+}
+
+
+.sg-editing-widget-slider-panel .sg-editing-widget-panel {
+	width: 		100%;
+	height: 	100%;
+}
+
+
+.sg-editing-widget-panel > .sg-widget-triangle-up {
+	top:  		-15px;;
+	left: 		0px;
+}
+
+.sg-editing-widget-slider-range {
+	position: 	absolute;
+	top:		0px;
+	left:		0px;
+	width: 		2px;
+	height: 	120px;
+
+	border-left: 14px solid rgb(183, 12, 29);
+	border-right: 14px solid rgb(183, 12, 29);
+	background-color: rgb(255,255,255);
+}
+
+.sg-editing-widget-slider-handle {
+	position: 	absolute;
+	left:  		-12px;
+	width: 		26px;
+	height: 	8px;
+	
+	background-color: rgb(255,255,255);
+}

+ 25 - 0
src/allplatforms/css/SuperGlue.css

@@ -0,0 +1,25 @@
+
+
+@font-face {
+    font-family: Dosis;
+    src: url("./fonts/dosis/Dosis-Regular.otf") format("opentype");
+}
+@font-face {
+    font-family: Dosis;
+    font-weight: bold;
+    src: url("./fonts/dosis/Dosis-Bold.otf") format("opentype");
+}
+@font-face {
+    font-family: Dosis;
+    font-weight: light;
+    src: url("./fonts/dosis/Dosis-Light.otf") format("opentype");
+}
+
+
+
+body {
+	cursor: 				default;
+	user-select: 			none;
+	-moz-user-select: 		none;
+	-webkit-user-select: 	none;
+}

+ 237 - 0
src/allplatforms/css/TextEditor.css

@@ -0,0 +1,237 @@
+
+#sg-editing-textEditor-container {
+	
+	position: absolute;
+	top: 0px;
+	left: 0px;
+
+}
+
+
+#sg-editing-textEditor {
+	
+	position: absolute;
+
+}
+
+/***********************
+* Text Editing Toolbar
+***********************/
+
+#textShapeToolbar {
+	position: absolute;
+	/*
+	top: -74px;
+	right: 0px;
+	*/
+	width: 300px;
+	/*
+	background: background: rgb(255,255,255);
+    background: -moz-linear-gradient(top,  rgba(255,255,255,1) 0%, rgba(239,239,239,1) 100%);
+    background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(255,255,255,1)), color-stop(100%,rgba(239,239,239,1)));
+    background: -webkit-linear-gradient(top,  rgba(255,255,255,1) 0%,rgba(239,239,239,1) 100%);
+    background: linear-gradient(to bottom,  rgba(255,255,255,1) 0%,rgba(239,239,239,1) 100%);
+	border: 2px solid rgb(255, 41, 61);
+	*/
+	-webkit-user-select: none;
+	-moz-user-select: none;
+	-ms-user-select: none;
+	user-select: none;
+	/*padding: 0 0 5px 5px;*/
+	padding: 0;
+}
+
+#textShapeToolbar .btn-group {
+	float: right;
+	position: relative;
+	height: 30px;
+	margin: 5px 0 0 5px;
+}
+
+#textShapeToolbar .dropdown-toggle {
+	padding-right: 10px;
+}
+
+#textShapeToolbar .dropdown-toggle.open {
+	background: #B10A1B;
+}
+
+#textShapeToolbar .dropdown-toggle.open:not(.right):after {
+	position: absolute;
+	left: 0;
+	bottom: -5px;
+	height: 6px;
+	width: 100%;
+	background: #B10A1B;
+	content: "";
+}
+
+#textShapeToolbar .dropdown-toggle .caret {
+	position: absolute;
+	right: 3px;
+	top: 12px;
+	border-right: 6px solid transparent;
+	border-left: 6px solid transparent;
+	border-top: 6px solid #fff;
+}
+
+#textShapeToolbar .dropdown-toggle .caretRight {
+	position: absolute;
+	right: 3px;
+	top: 9px;
+	border-bottom: 6px solid transparent;
+	border-left: 6px solid #fff;
+	border-top: 6px solid transparent;
+}
+
+#textShapeToolbar .dropdown-menu {
+	position: absolute;
+	top: 32px;
+	left: -20px;
+	width: 200px;
+	max-height: 200px;
+	margin: 0;
+	padding: 0;
+	overflow: auto;
+	background: #B10A1B;
+	z-index: 1;
+	border: 2px solid #B10A1B;
+	list-style: none;
+	display: none;
+}
+
+#textShapeToolbar .dropdown-menu.small {
+	width: 84px;
+}
+
+#textShapeToolbar .dropdown-menu.input-append {
+	display: block;
+	background: rgb(255, 95, 113);
+	color: #fff;
+	padding: 5px;
+}
+
+#textShapeToolbar .dropdown-menu.input-append input {
+	color: #000;
+}
+
+#textShapeToolbar .dropdown-menu.active {
+	display: block;
+}
+
+#textShapeToolbar .dropdown-menu li, #textShapeToolbar .dropdown-menu li a {
+	display: block;
+	color: #fff;
+	height: auto;
+}
+
+#textShapeToolbar .dropdown-menu li a {
+	padding: 3px 6px;
+	border-bottom: 1px solid #fff;
+	text-decoration: none;
+}
+
+#textShapeToolbar .dropdown-menu li a.wysihtml5-command-active {
+	background: rgb(255, 41, 61);
+}
+
+#textShapeToolbar .dropdown-menu li a.wysihtml5-command-active:hover {
+	background: #B10A1B;
+}
+
+#textShapeToolbar a {
+	display: inline-block;
+	background: rgb(255, 41, 61);
+	cursor: pointer;
+	height: 30px;
+}
+
+#textShapeToolbar a:hover {
+	background: #B10A1B;
+}
+
+#textShapeToolbar a.wysihtml5-command-active {
+	background: #B10A1B;
+}
+
+#textShapeToolbar a i {
+	display: inline-block;
+	width: 30px;
+	height: 30px;
+	background-size: 30px 30px;
+	background-position: center;
+	background-repeat: no-repeat;
+}
+
+#textShapeToolbar a i.icon-font {
+	background-image: url(./icons/font.face.svg);
+}
+
+#textShapeToolbar a i.icon-text-height {
+	background-image: url(./icons/font.size.svg);
+}
+
+#textShapeToolbar a i.icon-bold {
+	background-image: url(./icons/font.style.bold.svg);
+}
+
+#textShapeToolbar a i.icon-italic {
+	background-image: url(./icons/font.style.italic.svg);
+}
+
+#textShapeToolbar a i.icon-strikethrough {
+	background-image: url(./icons/font.style.strikeout.svg);
+}
+
+#textShapeToolbar a i.icon-underline {
+	background-image: url(./icons/font.style.underline.svg);
+}
+
+#textShapeToolbar a i.icon-list-ul {
+	background-image: url(./icons/list.unordered.svg);
+}
+
+#textShapeToolbar a i.icon-list-ol {
+	background-image: url(./icons/list.ordered.svg);
+}
+
+#textShapeToolbar a i.icon-align-left {
+	background-image: url(./icons/align.left.svg);
+}
+
+#textShapeToolbar a i.icon-align-center {
+	background-image: url(./icons/align.center.svg);
+}
+
+#textShapeToolbar a i.icon-align-right {
+	background-image: url(./icons/align.right.svg);
+}
+
+#textShapeToolbar a i.icon-align-justify {
+	background-image: url(./icons/align.justify.svg);
+}
+
+#textShapeToolbar a i.icon-indent-right {
+	background-image: url(./icons/indent.right.svg);
+}
+
+#textShapeToolbar a i.icon-indent-left {
+	background-image: url(./icons/indent.left.svg);
+}
+
+#textShapeToolbar a.hyperlink i.icon-link {
+	background-image: url(./icons/link.svg);
+}
+
+#textShapeToolbar a.hyperlink.wysihtml5-command-active i.icon-link {
+	background-image: url(./icons/unlink.svg);
+}
+/*
+#textShapeToolbar a i.icon-remove-link {
+	background-image: url(./icons/unlink.svg);
+}
+*/
+
+#textShapeToolbar a i.icon-color {
+	background-image: url(./icons/font.color.svg);
+}

+ 90 - 0
src/allplatforms/css/Widget.css

@@ -0,0 +1,90 @@
+
+.sg-editing-widget-container {
+	pointer-events: 	all;
+	cursor: 			default !important;
+	position: 			relative;
+	width: 				30px;
+	height: 			30px;
+	margin: 			0px;
+	font-family: 		Dosis, sans-serif;
+	letter-spacing: 	1px;
+
+}
+
+.sg-editing-widget-button {
+	position: 			absolute;
+	cursor: 			pointer;
+	width: 				30px;
+	height: 			30px;
+	margin: 			0;
+	padding: 			1px 6px; /* icon files are 18x28 */
+	border: 			none;
+	outline: 			none;
+	overflow: 			visible;
+	
+	background-color: 		rgb(255, 41, 61);
+	background-repeat: 		no-repeat;
+	background-size: 		30px auto;
+	background-position:	center;
+	-webkit-appearance: 	button;
+}
+
+.sg-editing-widget-button::-moz-focus-inner {
+	border: 0;
+}
+
+.sg-editing-widget-container.active > .sg-editing-widget-button {
+	background-color: 		rgb(183, 12, 29);
+}
+
+
+.sg-editing-selection-widget-menu-right > .sg-editing-widget-container {
+	margin-top: 		5px;
+}
+
+.sg-editing-selection-widget-menu-bottom > .sg-editing-widget-container {
+	float: 				right;
+	margin-left: 		5px;
+}
+
+
+
+
+.sg-editing-widget-panel {
+	position: 			absolute;
+	top: 				15px;
+	left: 				0px;
+	width: 				100%;
+	height: 			34px;
+	background-color: 	rgb(183, 12, 29);
+}
+
+.sg-editing-widget-panel .sg-editing-widget-button {
+	background-color: 		rgb(183, 12, 29);
+}
+
+.sg-widget-triangle-up {
+	position: 		absolute;
+	width: 			0;
+	height: 		0;
+	border-style: 	solid;
+	border-width: 	0 15px 15px 15px;
+	border-color: 	transparent transparent rgb(183, 12, 29) transparent;
+}
+
+.sg-widget-triangle-left {
+	position: 		absolute;
+	width: 			0;
+	height: 		0;
+	border-style: 	solid;
+	border-width: 	15px 15px 15px 0;
+	border-color: 	transparent rgb(183, 12, 29) transparent transparent;
+}
+
+.sg-editing-widget-container input[type="text"] {
+	position: 		absolute;
+	border:			none;
+	margin: 		0px;
+	padding:		4px;
+	height: 		22px;
+}

+ 3 - 0
src/allplatforms/css/WidgetBackgroundColor.css

@@ -0,0 +1,3 @@
+#sg-editing-widget-backgroundColor {
+	background-image: url("icons/backgroundColor.svg");
+}

+ 4 - 0
src/allplatforms/css/WidgetBorder.css

@@ -0,0 +1,4 @@
+#sg-editing-widget-border {
+	background-image: url("icons/border.svg");
+}
+

+ 3 - 0
src/allplatforms/css/WidgetBorderColor.css

@@ -0,0 +1,3 @@
+#sg-editing-widget-borderColor {
+	background-image: url("icons/borderColor.svg");
+}

+ 3 - 0
src/allplatforms/css/WidgetBorderRadius.css

@@ -0,0 +1,3 @@
+#sg-editing-widget-borderRadius {
+	background-image: url("icons/borderRadius.svg");
+}

+ 3 - 0
src/allplatforms/css/WidgetCopy.css

@@ -0,0 +1,3 @@
+#sg-editing-widget-copy {
+	background-image: url("icons/copy.svg");
+}

+ 3 - 0
src/allplatforms/css/WidgetDelete.css

@@ -0,0 +1,3 @@
+#sg-editing-widget-delete {
+	background-image: url("icons/delete.svg");
+}

+ 3 - 0
src/allplatforms/css/WidgetEditHTML.css

@@ -0,0 +1,3 @@
+#sg-editing-widget-editHTML {
+	background-image: url("icons/editHTML.svg");
+}

+ 45 - 0
src/allplatforms/css/WidgetIFrame.css

@@ -0,0 +1,45 @@
+#sg-editing-widget-iframe {
+	background-image: url("icons/iframe.svg");
+}
+
+#sg-editing-widget-iframe-panel {
+	position: 			absolute;
+	top: 				30px;
+	left: 				0px;
+	width: 				246px;
+	height: 			32px;
+}
+
+
+
+
+.sg-editing-widget-panel > .sg-widget-triangle-up {
+	top:  		-15px;;
+	left: 		0px;
+}
+
+#sg-editing-widget-iframe-input {
+	position: 	absolute;
+	top:		2px;
+	left:		2px;
+	width:		170px;
+
+}
+
+#sg-editing-widget-iframe-chooseFile {
+	top:		2px;
+	left:		182px;
+}
+
+#sg-editing-widget-iframe-clear {
+	top:		2px;
+	left:		214px;
+}
+
+#sg-editing-widget-iframe-chooseFile {
+	background-image: url("icons/fileManager.svg");
+}
+
+#sg-editing-widget-iframe-clear {
+	background-image: url("icons/clear.svg");
+}

+ 70 - 0
src/allplatforms/css/WidgetImgDimensions.css

@@ -0,0 +1,70 @@
+#sg-editing-widget-imgDimensions {
+	background-image: url("icons/imgDimensions.svg");
+}
+
+#sg-editing-widget-imgDimensions-panel {
+	position: 			absolute;
+	top: 				30px;
+	left: 				0px;
+	width: 				66px;
+	height: 			98px;
+}
+
+#sg-editing-widget-imgDimensions-panel > .sg-editing-widget-panel {
+	height: 			98px;
+}
+
+
+
+.sg-editing-widget-panel > .sg-widget-triangle-up {
+	top:  		-15px;;
+	left: 		0px;
+}
+
+
+
+
+#sg-editing-widget-imgDimensions-panel .sg-editing-widget-button {
+	background-color: rgb(255, 41, 61);
+}
+
+#sg-editing-widget-imgDimensions-panel .sg-editing-widget-button:hover {
+	background-color: rgb(183, 12, 29);
+}
+
+
+#sg-editing-widget-imgDimensions-tile {
+	top: 2px;
+	left: 2px;
+	background-image: url("icons/tile.svg");
+}
+
+#sg-editing-widget-imgDimensions-tileX {
+	top: 34px;
+	left: 2px;
+	background-image: url("icons/tileX.svg");
+}
+
+#sg-editing-widget-imgDimensions-tileY {
+	top: 66px;
+	left: 2px;
+	background-image: url("icons/tileY.svg");
+}
+
+#sg-editing-widget-imgDimensions-stretch {
+	top: 2px;
+	left: 34px;
+	background-image: url("icons/stretch.svg");
+}
+
+#sg-editing-widget-imgDimensions-stretchAspectRatio {
+	top: 34px;
+	left: 34px;
+	background-image: url("icons/stretchAspectRatio.svg");
+}
+
+#sg-editing-widget-imgDimensions-aspectRatio {
+	top: 66px;
+	left: 34px;
+	background-image: url("icons/aspectRatio.svg");
+}

+ 45 - 0
src/allplatforms/css/WidgetImgLink.css

@@ -0,0 +1,45 @@
+#sg-editing-widget-imgLink {
+	background-image: url("icons/imgLink.svg");
+}
+
+#sg-editing-widget-imgLink-panel {
+	position: 			absolute;
+	top: 				30px;
+	left: 				0px;
+	width: 				246px;
+	height: 			32px;
+}
+
+
+
+
+.sg-editing-widget-panel > .sg-widget-triangle-up {
+	top:  		-15px;;
+	left: 		0px;
+}
+
+#sg-editing-widget-imgLink-input {
+	position: 	absolute;
+	top:		2px;
+	left:		2px;
+	width:		170px;
+
+}
+
+#sg-editing-widget-imgLink-chooseFile {
+	top:		2px;
+	left:		182px;
+}
+
+#sg-editing-widget-imgLink-clear {
+	top:		2px;
+	left:		214px;
+}
+
+#sg-editing-widget-imgLink-chooseFile {
+	background-image: url("icons/fileManager.svg");
+}
+
+#sg-editing-widget-imgLink-clear {
+	background-image: url("icons/clear.svg");
+}

Some files were not shown because too many files changed in this diff