From 9e1ee9114e5364199acf1abb54798c81c14b9cbe Mon Sep 17 00:00:00 2001 From: Ananya Sen Date: Wed, 16 May 2012 11:01:20 -0700 Subject: single selection copy/paste canvas 2d shapes [rectangle and circle] Signed-off-by: Ananya Sen --- js/controllers/clipboard-controller.js | 105 ++++++++++++++++++++++++++++----- js/controllers/styles-controller.js | 70 +++++++++++++++++----- 2 files changed, 144 insertions(+), 31 deletions(-) (limited to 'js') diff --git a/js/controllers/clipboard-controller.js b/js/controllers/clipboard-controller.js index b023d37c..6ac90d96 100644 --- a/js/controllers/clipboard-controller.js +++ b/js/controllers/clipboard-controller.js @@ -8,7 +8,10 @@ No rights, expressed or implied, whatsoever to this software are provided by Mot // var Montage = require("montage/core/core").Montage, Component = require("montage/ui/component").Component, - NJUtils = require("js/lib/NJUtils").NJUtils; + NJUtils = require("js/lib/NJUtils").NJUtils, + World = require("js/lib/drawing/world").World, + ShapesController = require("js/controllers/elements/shapes-controller").ShapesController, + ShapeModel = require("js/models/shape-model").ShapeModel; var ClipboardController = exports.ClipboardController = Montage.create(Component, { hasTemplate: { @@ -29,6 +32,10 @@ var ClipboardController = exports.ClipboardController = Montage.create(Component } }, + copiedObjects:{ + value: null + }, + _copyFlag:{ value:false }, @@ -65,7 +72,11 @@ var ClipboardController = exports.ClipboardController = Montage.create(Component if(this.application.ninja.documentController.activeDocument.currentView === "code") return; if(this.application.ninja.selectedElements.length > 0){ - //handling 1 selected element + //handling 1 selected element for now + + if(this.application.ninja.selectedElements[0].tagName === "CANVAS"){ + this.copiedObjects = this.application.ninja.selectedElements[0]; + } elem = this.application.ninja.selectedElements[0]; originalStyleAttr = elem.getAttribute("style");//preserve the current styles @@ -100,6 +111,8 @@ var ClipboardController = exports.ClipboardController = Montage.create(Component value:function(clipboardEvent){ if(this.application.ninja.documentController.activeDocument.currentView === "code") return; + //TODO: return if stage is not focussed + var clipboardData = clipboardEvent.clipboardData, htmlData = clipboardData.getData("text/html"), textData = clipboardData.getData("text/plain"); @@ -121,41 +134,103 @@ var ClipboardController = exports.ClipboardController = Montage.create(Component data = htmlData || textData; - if(data){ + if(htmlData){ //TODO: cleanse HTML this.application.ninja.selectedElements.length = 0; NJevent("selectionChange", {"elements": this.application.ninja.selectedElements, "isDocument": true} ); - clipboardHelper.innerHTML = data;//add the copied html to generate the nodes + clipboardHelper.innerHTML = htmlData;//add the copied html to generate the nodes while(clipboardHelper.hasChildNodes()){ - if(clipboardHelper.lastChild.tagName !== "META") { - node = clipboardHelper.removeChild(clipboardHelper.lastChild); + if(clipboardHelper.lastChild.tagName === "META") { + clipboardHelper.removeChild(clipboardHelper.lastChild);//remove unnecesary meta tag + } + else if (clipboardHelper.lastChild.tagName === "CANVAS"){//temporary - we probably won't need to serialize this to the system clipboard - node.removeAttribute("style");//remove the computed styles attribute which is placed only for pasting to external applications + //only handling 1 canvas for POC - //get class string while copying .... generate styles from class - styles = {"top":"100px", "left":"100px"};//get real stage center coordinates - this.pastePositioned(node, styles); + //clone copied canvas + var canvas = document.application.njUtils.make("canvas", this.copiedObjects.className, this.application.ninja.currentDocument); + canvas.width = this.copiedObjects.width; + canvas.height = this.copiedObjects.height; + //end - clone copied canvas + + if (!canvas.getAttribute( "data-RDGE-id" )) canvas.setAttribute( "data-RDGE-id", NJUtils.generateRandom() ); + document.application.njUtils.createModelWithShape(canvas); +// canvas.elementModel.controller = ShapesController; +// if(!canvas.elementModel.shapeModel) { +// canvas.elementModel.shapeModel = Montage.create(ShapeModel); +// } + styles = canvas.elementModel.data || {}; + styles.top = "" + (this.application.ninja.elementMediator.getProperty(this.copiedObjects, "top", parseInt) - 50) + "px"; + styles.left = "" + (this.application.ninja.elementMediator.getProperty(this.copiedObjects, "left", parseInt) - 50) + "px"; + + this.application.ninja.elementMediator.addElements(canvas, styles, false); + + var world, worldData = this.copiedObjects.elementModel.shapeModel.GLWorld.exportJSON(); + if(worldData) + { + + var jObj; + var index = worldData.indexOf( ';' ); + if ((worldData[0] === 'v') && (index < 24)) + { + // JSON format. separate the version info from the JSON info + //var vStr = importStr.substr( 0, index+1 ); + var jStr = worldData.substr( index+1 ); + jObj = JSON.parse( jStr ); + world = new World(canvas, jObj.useWebGl); + canvas.elementModel.shapeModel.GLWorld = world; + canvas.elementModel.shapeModel.useWebGl = jObj.useWebGl; + world.importJSON(jObj); + this.application.ninja.currentDocument.buildShapeModel( canvas.elementModel, world ); + } + } - //this.pasteInPlace(temp);//does not work now + NJevent("elementAdded", canvas); -// this.application.ninja.documentController.activeDocument.documentRoot.insertBefore(temp, this.application.ninja.documentController.activeDocument.documentRoot.firstChild); -// NJUtils.makeModelFromElement(temp); -// NJevent("elementAdded", temp); + clipboardHelper.removeChild(clipboardHelper.lastChild); + } + else if(clipboardHelper.lastChild.nodeType === 3){//TextNode + node = clipboardHelper.removeChild(clipboardHelper.lastChild); + + //USE styles controller to create the styles of the div and span + var doc = this.application.ninja.currentDocument ? this.application.ninja.currentDocument._document : document; + var aspan = doc.createElement("span"); + aspan.appendChild(node); + var adiv = doc.createElement("div"); + adiv.appendChild(aspan); + styles = {"top":"100px", "left":"100px"}; + + this.pastePositioned(node, styles); } else { - clipboardHelper.removeChild(clipboardHelper.lastChild);//remove unnecesary meta tag + node = clipboardHelper.removeChild(clipboardHelper.lastChild); + + if(node.removeAttribute) {node.removeAttribute("style");}//remove the computed styles attribute which is placed only for pasting to external applications + + //get class string while copying .... generate styles from class + //styles = {"top":"100px", "left":"100px"}; + + this.pastePositioned(node, styles); } + } this.application.ninja.documentController.activeDocument.needsSave = true; + }else if(textData){ + + //USE styles controller to create the styles of the div and span + clipboardHelper.innerHTML = "
"+ textData +"
";//add the copied html to generate the nodes + node = clipboardHelper.removeChild(clipboardHelper.lastChild); + styles = {"top":"100px", "left":"100px"};//get real stage center coordinates + this.pastePositioned(node, styles); } } diff --git a/js/controllers/styles-controller.js b/js/controllers/styles-controller.js index 647c0870..cbc00676 100755 --- a/js/controllers/styles-controller.js +++ b/js/controllers/styles-controller.js @@ -94,11 +94,18 @@ var stylesController = exports.StylesController = Montage.create(Component, { // Returns null if sheet not found (as in non-ninja projects) // Setter will handle null case this.defaultStylesheet = this.getSheetFromElement(this.CONST.DEFAULT_SHEET_ID); - - //debugger; + + this.userStyleSheets = nj.toArray(document._document.styleSheets).filter(function(sheet) { + return sheet !== this._stageStylesheet; + }, this); + + NJevent('styleSheetsReady', this); }, enumerable : false }, + userStyleSheets : { + value : null + }, _stageStylesheet : { value : null }, @@ -183,6 +190,7 @@ var stylesController = exports.StylesController = Montage.create(Component, { ///// attach specificity to rule object ///// if rule is css keyframes, return rule and don't attach specificity if (rule instanceof WebKitCSSKeyframesRule) { + return rule; } rule[this.CONST.SPECIFICITY_KEY] = this.getSpecificity(rule.selectorText); @@ -209,10 +217,12 @@ var stylesController = exports.StylesController = Montage.create(Component, { } var selectorToOverride = getSelector.bind(this)(element, ruleToOverride), - overrideData, rule; + overrideData, rule, isRuleLocked; + + isRuleLocked = this.isSheetLocked(ruleToOverride.parentStyleSheet); ///// Get the overriding selector and className - overrideData = this.createOverrideSelector(selectorToOverride, element.nodeName); + overrideData = this.createOverrideSelector(selectorToOverride, element.nodeName, isRuleLocked); ///// Create new rule with selector and insert it after the rule we're overriding rule = this.addRule(overrideData.selector + ' { }', this.getRuleIndex(ruleToOverride)+1); @@ -226,7 +236,7 @@ var stylesController = exports.StylesController = Montage.create(Component, { }, createOverrideSelector : { - value: function(selectorToOverride, classPrefix, className) { + value: function(selectorToOverride, classPrefix, increaseSpecificity, className) { var tokens = selectorToOverride.split(/\s/), newClass = className || this.generateClassName(classPrefix, true), lastToken, pseudoSplit, base, pseudo, newToken, newSelector; @@ -247,10 +257,19 @@ var stylesController = exports.StylesController = Montage.create(Component, { if(base.indexOf('#') !== -1) { newToken = base + '.' + newClass + pseudo; } else { - ///// Replace last class or attribute selector - ///// Get everything right before the last class or attribute selector - ///// to support compound selector values: (i.e. .firstClass.secondClass) - newToken = base.substring(0, Math.max(base.lastIndexOf('.'), base.lastIndexOf('['))); + if(increaseSpecificity) { + ///// Increases specificity by one class selector + ///// We'll do a direct append to the base class + ///// if we want to increase the specificity + newToken = base; + } else { + ///// Maintains original specificity + ///// Replace last class or attribute selector + ///// Get everything right before the last class or attribute selector + ///// to support compound selector values: (i.e. .firstClass.secondClass) + newToken = base.substring(0, Math.max(base.lastIndexOf('.'), base.lastIndexOf('['))); + } + ///// Append the generated class newToken += '.' + newClass + pseudo; } @@ -795,7 +814,9 @@ var stylesController = exports.StylesController = Montage.create(Component, { ///// method to apply/test the new value dec.setProperty(property, value, priority); - this.styleSheetModified(rule.parentStyleSheet); + if(rule.parentStyleSheet) { + this.styleSheetModified(rule.parentStyleSheet); + } ///// Return browser value for value we just set return dec.getPropertyValue(property); @@ -969,12 +990,13 @@ var stylesController = exports.StylesController = Montage.create(Component, { var doc = element.ownerDocument, useImportant = false, cache = this._getCachedRuleForProperty(element, property), - dominantRule, override, className, browserValue; + dominantRule, override, className, browserValue, cacheMatchesMany; if(cache) { ///// We've cached the rule for this property! //console.log('Styles Controller :: setElementStyle - We found the cached rule!'); dominantRule = cache; + cacheMatchesMany = this.matchesMultipleElements(dominantRule, doc); } else { ///// Use Dominant Rule logic to find the right place to add the style ///// Pass "true" to method to return an override object, which @@ -982,7 +1004,7 @@ var stylesController = exports.StylesController = Montage.create(Component, { dominantRule = this.getDominantRuleForElement(element, property, true, isStageElement); } - + ///// Did we find a dominant rule? if(!dominantRule) { ///// No. This means there was no rule with this property, and no @@ -1000,6 +1022,13 @@ var stylesController = exports.StylesController = Montage.create(Component, { useImportant = dominantRule.useImportant; dominantRule = override.rule; this.addClass(element, override.className); + } else if(cacheMatchesMany) { + ///// Only happens when the cached rule applies to multiple + ///// elements - we must create override + override = this.createOverrideRule(dominantRule, element); + useImportant = !!dominantRule.style.getPropertyPriority(property); + dominantRule = override.rule; + this.addClass(element, override.className); } @@ -1007,7 +1036,7 @@ var stylesController = exports.StylesController = Montage.create(Component, { browserValue = this.setStyle(dominantRule, property, value, useImportant); ///// Only cache the dominant rule if the style value was valid, and not already cached - if(browserValue && !cache) { + if(browserValue && (!cache || cacheMatchesMany)) { this._setCachedRuleForProperty(element, property, dominantRule); } @@ -1242,8 +1271,12 @@ var stylesController = exports.StylesController = Montage.create(Component, { doc.head.appendChild(sheetElement); sheet = this.getSheetFromElement(sheetElement, doc); + this.userStyleSheets.push(sheet); + this.styleSheetModified(sheet); + NJevent('newStyleSheet', sheet); + return sheet; } }, @@ -1266,6 +1299,12 @@ var stylesController = exports.StylesController = Montage.create(Component, { } }, + isSheetLocked : { + value: function(sheet) { + return !!sheet.ownerNode.dataset['ninjaFileReadOnly']; + } + }, + ///// Style Sheet Modified ///// Method to call whenever a stylesheet change is made ///// Dispatches an event, and keeps list of dirty style sheets @@ -1306,11 +1345,10 @@ var stylesController = exports.StylesController = Montage.create(Component, { this.dirtyStyleSheets.length = 0; if(doc) { - var stillDirty = this.dirtyStyleSheets.filter(function(sheet) { + this.dirtyStyleSheets = null; + this.dirtyStyleSheets = this.dirtyStyleSheets.filter(function(sheet) { return sheet.document !== doc; }); - this.dirtyStyleSheets = null; - this.dirtyStyleSheets = stillDirty; } -- cgit v1.2.3