/* <copyright> This file contains proprietary software owned by Motorola Mobility, Inc.<br/> No rights, expressed or implied, whatsoever to this software are provided by Motorola Mobility, Inc. hereunder.<br/> (c) Copyright 2011 Motorola Mobility, Inc. All Rights Reserved. </copyright> */ //////////////////////////////////////////////////////////////////////// // var Montage = require("montage/core/core").Montage, TextDocument = require("js/document/text-document").TextDocument, NJUtils = require("js/lib/NJUtils").NJUtils; //////////////////////////////////////////////////////////////////////// // exports.HTMLDocument = Montage.create(TextDocument, { _selectionExclude: { value: null, enumerable: false }, _htmlTemplateUrl: { value: "js/document/templates/montage-html/index.html", enumerable: false}, _iframe: { value: null, enumerable: false }, _server: { value: null, enumerable: false }, _templateDocument: { value: null, enumerable: false }, _selectionModel: { value: [], enumerable: false }, _undoModel: { value: { "queue" : [], "position" : 0 }, enumerable: false}, _document: { value: null, enumerable: false }, _documentRoot: { value: null, enumerable: false }, _stageBG: { value: null, enumerable: false }, _window: { value: null, enumerable: false }, _styles: { value: null, enumerable: false }, _stylesheets: { value: null, enumerable: false }, _stageStyleSheetId : { value: 'nj-stage-stylesheet', enumerable: false }, _userDocument: { value: null, enumerable: false }, _htmlSource: {value: "<html></html>", enumerable: false}, _glData: {value: null, enumerable: false }, _userComponents: { value: {}, enumarable: false}, _elementCounter: { value: 1, enumerable: false }, _snapping : { value: true, enumerable: false }, _layoutMode: { value: "all", enumerable: false }, _draw3DGrid: { value: false, writable: true }, _swfObject: { value: false, enumerable: false }, _zoomFactor: { value: 100, enumerable: false }, cssLoadInterval: { value: null, enumerable: false }, _savedLeftScroll: {value:null}, _savedTopScroll: {value:null}, _codeViewDocument:{ writable: true, enumerable: true, value:null }, // GETTERS / SETTERS codeViewDocument:{ get: function() { return this._codeViewDocument; }, set: function(value) { this._codeViewDocument = value} }, savedLeftScroll:{ get: function() { return this._savedLeftScroll; }, set: function(value) { this._savedLeftScroll = value} }, savedTopScroll:{ get: function() { return this._savedTopScroll; }, set: function(value) { this._savedTopScroll = value} }, selectionExclude: { get: function() { return this._selectionExclude; }, set: function(value) { this._selectionExclude = value; } }, iframe: { get: function() { return this._iframe; }, set: function(value) { this._iframe = value; } }, server: { get: function() { return this._server; }, set: function(value) { this._server = value; } }, selectionModel: { get: function() { return this._selectionModel; }, set: function(value) { this._selectionModel = value; } }, undoModel: { get: function() { return this._undoModel; }, set: function(value) { this._undoModel.queue = value.queue; this._undoModel.position = value.position; } }, documentRoot: { get: function() { return this._documentRoot; }, set: function(value) { this._documentRoot = value; } }, stageBG: { get: function() { return this._stageBG; }, set: function(value) { this._stageBG = value; } }, elementCounter: { set: function(value) { this._elementCounter = value; }, get: function() { return this._elementCounter; } }, snapping: { get: function() { return this._snapping; }, set: function(value) { if(this._snapping !== value) { this._snapping = value; } } }, // TODO SEND THE EVENT --> Redraw the desired layout layoutMode: { get: function() { return this._layoutMode; }, set: function(mode) { this._layoutMode = mode; } }, draw3DGrid: { get: function() { return this._draw3DGrid; }, set: function(value) { if(this._draw3DGrid !== value) { this._draw3DGrid = value; } } }, userComponents: { get: function() { return this._userComponents; } }, // _drawUserComponentsOnOpen:{ // value:function(){ // for(var i in this._userComponentSet){ // console.log(this._userComponentSet[i].control) // this._userComponentSet[i].control.needsDraw = true; // } // } // }, glData: { get: function() { var elt = this.iframe; var elt = this.iframe.contentWindow.document.getElementById("UserContent"); this._glData = null; if (elt) { this._glData = new Array(); this.collectGLData( elt, this._glData ); } return this._glData }, set: function(value) { var nWorlds = value.length; for (var i=0; i<nWorlds; i++) { var importStr = value[i]; var startIndex = importStr.indexOf( "id: " ); if (startIndex >= 0) { var endIndex = importStr.indexOf( "\n", startIndex ); if (endIndex > 0) { var id = importStr.substring( startIndex+4, endIndex ); var canvas = this.iframe.contentWindow.document.getElementById( id ); if (canvas) { if (!canvas.elementModel) { NJUtils.makeElementModel(canvas, "Canvas", "shape", true); } if (canvas.elementModel) { if (canvas.elementModel.shapeModel.GLWorld) canvas.elementModel.shapeModel.GLWorld.clearTree(); var world = new GLWorld( canvas ); canvas.elementModel.shapeModel.GLWorld = world; world.import( importStr ); } } } } } } }, zoomFactor: { get: function() { return this._zoomFactor; }, set: function(value) { this._zoomFactor = value; } }, /** * Add a reference to a component instance to the userComponents hash using the * element UUID */ setComponentInstance: { value: function(instance, el) { this.userComponents[el.uuid] = instance; } }, /** * Returns the component instance obj from the element */ getComponentFromElement: { value: function(el) { if(el) { if(el.uuid) return this.userComponents[el.uuid]; } else { return null; } } }, //////////////////////////////////////////////////////////////////// // initialize: { value: function(file, uuid, iframe, callback) { this.application.ninja.documentController._hackRootFlag = false; // this._userDocument = file; // this.init(file.name, file.uri, file.extension, iframe, uuid, callback); // this.iframe = iframe; this.selectionExclude = ["HTML", "BODY", "Viewport", "UserContent", "stageBG"]; this.currentView = "design"; // this.iframe.src = this._htmlTemplateUrl; this.iframe.addEventListener("load", this, true); } }, //////////////////////////////////////////////////////////////////// collectGLData: { value: function( elt, dataArray ) { if (elt.elementModel && elt.elementModel.shapeModel && elt.elementModel.shapeModel.GLWorld) { var data = elt.elementModel.shapeModel.GLWorld.export(); dataArray.push( data ); } if (elt.children) { var nKids = elt.children.length; for (var i=0; i<nKids; i++) { var child = elt.children[i]; this.collectGLData( child, dataArray ); } } } }, // OLD inExclusion: { value: function(element) { if(this._selectionExclude.indexOf(element.id) === -1) { if(this._selectionExclude.indexOf(element.nodeName) === -1) { return -1; } } else if (this._selectionExclude.indexOf(element.id) === -1) { return -1; } else { return 1; } } }, /** * Return the specified inline attribute from the element. */ GetElementAttribute: { value: function(element, attribute) { var value; if(attribute === "src") { return element[attribute].replace(window.location.href, ''); } value = element[attribute]; if(value !== undefined) return value; // if(value || value === false) return [value, "inline"]; // 3. //value = this._document.defaultView.getComputedStyle(element,null).getPropertyValue(attribute); //if(value) return value; return null; } }, GetElementStyle: { value: function(element, style) { // return this._queryStylesheets(element, style); } }, SetStyle: { value: function(type, selector, style, value) { try { for(var j=0; j<this._stylesheets.length;j++){ for(var i=0; i<this._stylesheets[j].cssRules.length;i++) { if(this._stylesheets[j].cssRules[i].selectorText === type + selector) { this._stylesheets[j].cssRules[i].style[style] = value; return true; } } } } catch(err) { console.log("Cannot change the style of selector: " + selector + " " + err); } } }, GetElementFromPoint: { value: function(x, y) { return this._window.getElement(x,y); } }, /* DOM Mutation Events: DOMActivate, DOMFocusIn, DOMFocusOut, DOMAttrModified, DOMCharacterDataModified, DOMNodeInserted, DOMNodeInsertedIntoDocument, DOMNodeRemoved, DOMNodeRemovedFromDocument, DOMSubtreeModified, DOMContentLoaded */ /* //TODO: Remove and clean up event listener (DOMSubtreeModified) _hackCount: { value: 0 }, */ //////////////////////////////////////////////////////////////////// // handleEvent: { value: function(event){ //TODO: Clean up, using for prototyping save this._templateDocument = {}; this._templateDocument.head = this.iframe.contentWindow.document.getElementById("userHead"); this._templateDocument.body = this.documentRoot = this.iframe.contentWindow.document.getElementById("UserContent"); //TODO: Remove, also for prototyping this.application.ninja.documentController._hackRootFlag = true; // //this.documentRoot = this.iframe.contentWindow.document.getElementById("UserContent"); this.stageBG = this.iframe.contentWindow.document.getElementById("stageBG"); this.stageBG.onclick = null; this._document = this.iframe.contentWindow.document; this._window = this.iframe.contentWindow; // if(!this.documentRoot.Ninja) this.documentRoot.Ninja = {}; // this._templateDocument.head.innerHTML = this._userDocument.content.head; this._templateDocument.body.innerHTML = this._userDocument.content.body; // Adding a handler for the main user document reel to finish loading. this._document.body.addEventListener("userTemplateDidLoad", this.userTemplateDidLoad.bind(this), false); /* this.iframe.contentWindow.document.addEventListener('DOMSubtreeModified', function (e) { */ //TODO: Remove events upon loading once //TODO: When written, the best way to initialize the document is to listen for the DOM tree being modified setTimeout(function () { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// if(this._document.styleSheets.length > 1) { this._styles = this._document.styleSheets[this._document.styleSheets.length - 1]; this._stylesheets = this._document.styleSheets; // Entire stlyesheets array //TODO Finish this implementation once we start caching Core Elements // Assign a model to the UserContent and add the ViewPort reference to it. NJUtils.makeElementModel(this.documentRoot, "Stage", "stage"); //this.documentRoot.elementModel.viewPort = this.iframe.contentWindow.document.getElementById("Viewport"); NJUtils.makeElementModel(this.stageBG, "Stage", "stage"); NJUtils.makeElementModel(this.iframe.contentWindow.document.getElementById("Viewport"), "Stage", "stage"); for(i = 0; i < this._stylesheets.length; i++) { if(this._stylesheets[i].ownerNode.id === this._stageStyleSheetId) { this.documentRoot.elementModel.defaultRule = this._stylesheets[i]; break; } } //Temporary create properties for each rule we need to save the index of the rule var len = this.documentRoot.elementModel.defaultRule.cssRules.length; for(var j = 0; j < len; j++) { //console.log(this.documentRoot.elementModel.defaultRule.cssRules[j].selectorText); if(this.documentRoot.elementModel.defaultRule.cssRules[j].selectorText === "*") { this.documentRoot.elementModel.transitionStopRule = this.documentRoot.elementModel.defaultRule.cssRules[j]; } else if(this.documentRoot.elementModel.defaultRule.cssRules[j].selectorText === "body") { this.documentRoot.elementModel.body = this.documentRoot.elementModel.defaultRule.cssRules[j]; } else if(this.documentRoot.elementModel.defaultRule.cssRules[j].selectorText === "#Viewport") { this.documentRoot.elementModel.viewPort = this.documentRoot.elementModel.defaultRule.cssRules[j]; } else if(this.documentRoot.elementModel.defaultRule.cssRules[j].selectorText === ".stageDimension") { this.documentRoot.elementModel.stageDimension = this.documentRoot.elementModel.defaultRule.cssRules[j]; } else if(this.documentRoot.elementModel.defaultRule.cssRules[j].selectorText === ".stageView") { this.documentRoot.elementModel.stageView = this.documentRoot.elementModel.defaultRule.cssRules[j]; } else if(this.documentRoot.elementModel.defaultRule.cssRules[j].selectorText === "#stageBG") { this.documentRoot.elementModel.stageBackground = this.documentRoot.elementModel.defaultRule.cssRules[j]; } } this.callback(this); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// }.bind(this), 1000); } }, //////////////////////////////////////////////////////////////////// // Handler for user content main reel. Gets called once the main reel of the template // gets deserialized. // Setting up the currentSelectedContainer to the document body. userTemplateDidLoad: { value: function(){ this.application.ninja.currentSelectedContainer = this.documentRoot; } }, //////////////////////////////////////////////////////////////////// _setSWFObjectScript: { value: function() { if(!this._swfObject) { /* var swfObj = document.createElement("script"); swfObj.type = "text/javascript"; swfObj.src = "../../user-document-templates/external-libs/swf-object/swfobject.js"; swfObj.id = "swfObject"; var head= this._document.getElementsByTagName('head')[0]; head.appendChild(swfObj); this._swfObject = true; */ } } }, //////////////////////////////////////////////////////////////////// // livePreview: { enumerable: false, value: function () { //TODO: Add logic to handle save before preview this.save(); //Launching 'blank' tab for testing movie chrome.tabs.create({url: this.application.ninja.coreIoApi.rootUrl+this.application.ninja.documentController._activeDocument.uri.split(this.application.ninja.coreIoApi.cloudData.root)[1]}); } }, //////////////////////////////////////////////////////////////////// // save: { enumerable: false, value: function () { //TODO: Add code view logic and also styles for HTML if (this.currentView === 'design') { return {mode: 'html', document: this._userDocument, webgl: this.glData, style: this._styles, head: this._templateDocument.head.innerHTML, body: this._templateDocument.body.innerHTML}; } else if (this.currentView === "code"){ //TODO: Would this get call when we are in code of HTML? } else { //Error } } } //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// });