/* <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, Component = require("montage/ui/component").Component, Uuid = require("montage/core/uuid").Uuid, HTMLDocument = require("js/document/html-document").HTMLDocument, TextDocument = require("js/document/text-document").TextDocument; // New Document Objects var Document = require("js/document/document-html").HtmlDocument; //////////////////////////////////////////////////////////////////////// // var DocumentController = exports.DocumentController = Montage.create(Component, { hasTemplate: { value: false }, _documents: { value: [] }, _hackRootFlag: { value: false }, _hackInitialStyles: { value: true }, _activeDocument: { value: null }, _iframeCounter: { value: 1, enumerable: false }, _iframeHolder: { value: null, enumerable: false }, _textHolder: { value: null, enumerable: false }, _codeMirrorCounter: {value: 1, enumerable: false}, activeDocument: { get: function() { return this._activeDocument; }, set: function(doc) { // if(!!this._activeDocument){ this._activeDocument.isActive = false;} this._activeDocument = doc; if(!!this._activeDocument){ if(this._documents.indexOf(doc) === -1) this._documents.push(doc); this._activeDocument.isActive = true; if(!!this._activeDocument.editor){ this._activeDocument.editor.focus(); } } } }, deserializedFromTemplate: { value: function() { this.eventManager.addEventListener("appLoaded", this, false); this.eventManager.addEventListener("executeFileOpen", this, false); this.eventManager.addEventListener("executeNewFile", this, false); this.eventManager.addEventListener("executeSave", this, false); this.eventManager.addEventListener("executeSaveAs", this, false); this.eventManager.addEventListener("executeSaveAll", this, false); this.eventManager.addEventListener("executeFileClose", this, false); this.eventManager.addEventListener("executeFileCloseAll", this, false); this.eventManager.addEventListener("styleSheetDirty", this, false); } }, //TODO: Ensure these APIs are not needed //////////////////////////////////////////////////////////////////// // handleWebRequest: { value: function (request) { //TODO: Check if frameId is proper if (this._hackRootFlag && request.parentFrameId !== -1) { //Checking for proper URL redirect (from different directories) if (request.url.indexOf('js/document/templates/banner') !== -1) { return {redirectUrl: this.application.ninja.coreIoApi.rootUrl+this.application.ninja.documentController.documentHackReference.root.split(this.application.ninja.coreIoApi.cloudData.root)[1]+request.url.split(chrome.extension.getURL('js/document/templates/banner/'))[1]}; } else if (request.url.indexOf('js/document/templates/html') !== -1) { return {redirectUrl: this.application.ninja.coreIoApi.rootUrl+this.application.ninja.documentController.documentHackReference.root.split(this.application.ninja.coreIoApi.cloudData.root)[1]+request.url.split(chrome.extension.getURL('js/document/templates/html/'))[1]}; } else { //Error, not a valid folder } } } }, //////////////////////////////////////////////////////////////////// // handleAppLoaded: { value: function() { //Checking for app to be loaded through extension var check; if (chrome && chrome.app) { check = chrome.app.getDetails(); } if (check !== null) { //Adding an intercept to resources loaded to ensure user assets load from cloud simulator chrome.webRequest.onBeforeRequest.addListener(this.handleWebRequest.bind(this), {urls: ["<all_urls>"]}, ["blocking"]); } } }, //////////////////////////////////////////////////////////////////// handleExecuteFileOpen: { value: function(event) { var pickerSettings = event._event.settings || {}; if (this.application.ninja.coreIoApi.cloudAvailable()) { pickerSettings.callback = this.openFileWithURI.bind(this); pickerSettings.pickerMode = "read"; pickerSettings.inFileMode = true; this.application.ninja.filePickerController.showFilePicker(pickerSettings); } } }, handleExecuteNewFile: { value: function(event) { var newFileSettings = event._event.settings || {}; if (this.application.ninja.coreIoApi.cloudAvailable()) { newFileSettings.callback = this.createNewFile.bind(this); this.application.ninja.newFileController.showNewFileDialog(newFileSettings); } } }, //////////////////////////////////////////////////////////////////// // handleExecuteSave: { value: function(event) { // if((typeof this.activeDocument !== "undefined") && this.application.ninja.coreIoApi.cloudAvailable()){ //Currently we don't need a callback handler //this.activeDocument.model.save(this.saveExecuted.bind(this)); this.activeDocument.model.save(); } else { //Error: cloud not available and/or no active document } } }, //////////////////////////////////////////////////////////////////// // saveExecuted: { value: function (value) { //File saved, any callbacks or events should go here } }, //////////////////////////////////////////////////////////////////// //TODO: Check for appropiate structures handleExecuteSaveAll: { value: function(event) { // if((typeof this.activeDocument !== "undefined") && this.application.ninja.coreIoApi.cloudAvailable()){ // this.activeDocument.model.saveAll(this.testCallback.bind(this)); //this.fileSaveResult.bind(this) } else { //Error: } } }, //////////////////////////////////////////////////////////////////// handleExecuteSaveAs: { value: function(event) { var saveAsSettings = event._event.settings || {}; if((typeof this.activeDocument !== "undefined") && this.application.ninja.coreIoApi.cloudAvailable()){ saveAsSettings.fileName = this.activeDocument.model.file.name; saveAsSettings.folderUri = this.activeDocument.model.file.uri.substring(0, this.activeDocument.model.file.uri.lastIndexOf("/")); saveAsSettings.callback = this.saveAsCallback.bind(this); this.application.ninja.newFileController.showSaveAsDialog(saveAsSettings); } } }, //////////////////////////////////////////////////////////////////// handleExecuteFileClose:{ value: function(event) { if (this.activeDocument) { // this.activeDocument.closeDocument(); this.closeFile(this.activeDocument); } } }, //////////////////////////////////////////////////////////////////// handleExecuteFileCloseAll:{ value: function(event) { var i=0; if(this.activeDocument && this.application.ninja.coreIoApi.cloudAvailable()){ while(this._documents.length > 0){ this.closeDocument(this._documents[this._documents.length -1].uuid); } } } }, //////////////////////////////////////////////////////////////////// // fileSaveResult: { value: function (result) { if((result.status === 204) || (result.status === 404)){//204=>existing file || 404=>new file... saved this.activeDocument.model.needsSave = false; if(this.application.ninja.currentDocument !== null){ //clear Dirty StyleSheets for the saved document this.application.ninja.stylesController.clearDirtyStyleSheets(this.application.ninja.currentDocument); } } } }, //////////////////////////////////////////////////////////////////// // createNewFile:{ value:function(newFileObj){ // if(!newFileObj) return; // this.application.ninja.ioMediator.fileNew(newFileObj.newFilePath, newFileObj.fileTemplateUri, this.openNewFileCallback.bind(this), newFileObj.template); } }, //////////////////////////////////////////////////////////////////// /** * Public method * doc contains: * type : file type, like js, css, etc * name : file name * source : file content * uri : file uri */ openNewFileCallback:{ value:function(doc){ var response = doc || null;//default just for testing if(!!response && response.success && (response.status!== 500) && !!response.uri){ this.isNewFilePath = true;//path identifier flag this.creatingNewFile = true;//flag for timeline to identify new file flow this.application.ninja.ioMediator.fileOpen(response.uri, this.openFileCallback.bind(this)); } else if(!!response && !response.success){ //Todo: restrict directory path to the sandbox, in the dialog itself alert("Unable to create file.\n [Error: Forbidden directory]"); } } }, openFileWithURI: { value: function(uriArrayObj) { var uri = "", fileContent = "", response=null, filename="", fileType="js"; if(!!uriArrayObj && !!uriArrayObj.uri && (uriArrayObj.uri.length > 0)){ uri = uriArrayObj.uri[0]; } //console.log("URI is: ", uri); if(!!uri){ this.application.ninja.ioMediator.fileOpen(uri, this.openFileCallback.bind(this)); } } }, //////////////////////////////////////////////////////////////////// // openFileCallback:{ value:function(response){ //TODO: Add UI to handle error codes, shouldn't be alert windows if(!!response && (response.status === 204)) { if((typeof this.isNewFilePath === 'undefined') || (this.isNewFilePath !== true)){//not from new file flow this.creatingNewFile = false; } this.isNewFilePath = false;//reset path identifier flag //Sending full response object this.openDocument(response); } else if (!!response && (response.status === 404)){ alert("Unable to open file.\n [Error: File does not exist]"); } else if (!!response && (response.status === 500)){ alert("Unable to open file.\n Check if Ninja Local Cloud is running."); } else{ alert("Unable to open file."); } } }, //////////////////////////////////////////////////////////////////// // saveAsCallback:{ value:function(saveAsDetails){ var fileUri = null, filename = saveAsDetails.filename, destination = saveAsDetails.destination; //update document metadata this.activeDocument.name = ""+filename; //prepare new file uri if(destination && (destination.charAt(destination.length -1) !== "/")){ destination = destination + "/"; } fileUri = destination+filename; this.activeDocument.uri = fileUri; //save a new file //use the ioMediator.fileSaveAll when implemented this.activeDocument._userDocument.name=filename; this.activeDocument._userDocument.root=destination; this.activeDocument._userDocument.uri=fileUri; this.application.ninja.ioMediator.fileSave(this.activeDocument.save(), this.fileSaveResult.bind(this)); // } }, //////////////////////////////////////////////////////////////////// openDocument: { value: function(doc) { var template, dimensions; if (doc.content.body.indexOf('Ninja-Banner Dimensions@@@') !== -1) { dimensions = (doc.content.body.split('Ninja-Banner Dimensions@@@'))[1].split('-->')[0].split('x'); dimensions = {width: parseInt(dimensions[0]), height: parseInt(dimensions[1])}; template = {type: 'banner', size: dimensions}; } // TODO: HACKS to remove this.documentHackReference = doc; document.getElementById("iframeContainer").style.overflow = "hidden"; // switch (doc.extension) { case 'html': //Open in designer view this._hackRootFlag = false; Montage.create(Document).init(doc, this, this._onOpenDocument, 'design', template); break; default: //Open in code view var code = Montage.create(TextDocument, {"source": {value: doc.content}}), docuuid = Uuid.generate(), textArea; textArea = this.application.ninja.stage.stageView.createTextAreaElement(docuuid); code.initialize(doc, docuuid, textArea, textArea.parentNode); //code.init(doc.name, doc.uri, doc.extension, null, docuuid); code.textArea.value = doc.content; this.application.ninja.stage.stageView.createTextView(code); break; } } }, //////////////////////////////////////////////////////////////////// openProjectWithURI: { value: function(uri) { console.log("URI is: ", uri); } }, textDocumentOpened: { value: function(doc) { this.application.ninja.stage.stageView.createTextView(doc); /* DocumentManager._hideCurrentDocument(); stageManagerModule.stageManager._scrollFlag = false; // TODO HACK to prevent type error on Hide/Show Iframe DocumentManager.activeDocument = doc; var type; switch(doc.documentType) { case "css" : type = "css"; break; case "js" : type = "javascript"; break; } DocumentManager._codeEditor.editor = CodeMirror.fromTextArea(doc.textArea, { lineNumbers: true, mode: type, onCursorActivity: function() { DocumentManager._codeEditor.editor.setLineClass(DocumentManager._codeEditor.hline, null); DocumentManager._codeEditor.hline = DocumentManager._codeEditor.editor.setLineClass(DocumentManager._codeEditor.editor.getCursor().line, "activeline"); } }); DocumentManager._codeEditor.hline = DocumentManager._codeEditor.editor.setLineClass(0, "activeline"); */ } }, closeFile: { value: function(document) { document.closeDocument(this, this.onCloseFile); } }, onCloseFile: { value: function(doc) { this._documents.splice(this._documents.indexOf(doc), 1); this._activeDocument = null; this.application.ninja.stage.stageView.hideRulers(); // document.getElementById("iframeContainer").style.display="block"; this.application.ninja.stage.hideCanvas(true); NJevent("closeDocument", doc.model.file.uri); //TODO: Delete object here } }, closeDocument: { value: function(id) { var doc = this._findDocumentByUUID(id); var closeDocumentIndex = this._findIndexByUUID(id); this._documents.splice(this._findIndexByUUID(id), 1); if(this.activeDocument.uuid === id && this._documents.length > 0) {//closing the active document tab var nextDocumentIndex = -1 ; if((this._documents.length > 0) && (closeDocumentIndex === 0)){ nextDocumentIndex = 0; }else if((this._documents.length > 0) && (closeDocumentIndex > 0)){ nextDocumentIndex = closeDocumentIndex - 1; } this.application.ninja.stage.stageView.switchDocument(this._documents[nextDocumentIndex]); if(typeof doc.stopVideos !== "undefined"){doc.stopVideos();} doc.container.parentNode.removeChild(doc.container); }else if(this._documents.length === 0){ // See above }else{//closing inactive document tab - just clear DOM if(typeof doc.pauseAndStopVideos !== "undefined"){ doc.pauseAndStopVideos(); } doc.container.parentNode.removeChild(doc.container); } NJevent("closeDocument", doc.uri); doc=null; } }, // Open document callback _onOpenDocument: { value: function(doc){ var currentDocument; if(this.activeDocument) { // There is a document currently opened currentDocument = this.activeDocument; //this.application.ninja.stage.stageView.restoreAllPanels(); } else { // There is no document opened // Show the rulers // TODO: Move this indo design view this.application.ninja.stage.stageView.showRulers(); // Show the canvas this.application.ninja.stage.hideCanvas(false); } // Set the active document this.activeDocument = doc; // Initialize the documentRoot styles this.initializeRootStyles(doc.documentRoot); // Flag to stop stylesheet dirty event this._hackInitialStyles = false; this.switchDocuments(currentDocument, doc, true); } }, switchDocuments: { value: function(currentDocument, newDocument, didCreate) { if(currentDocument) { currentDocument.serializeDocument(); this.application.ninja.selectionController._selectionContainer = null; currentDocument.model.views.design.propertiesPanel.clear(); currentDocument.model.views.design.hide(); } this.application.ninja.stage.clearAllCanvas(); if(didCreate) { newDocument.model.views.design.iframe.style.opacity = 1; NJevent("onOpenDocument", newDocument); } else { this.activeDocument = newDocument; newDocument.model.views.design.show(); newDocument.deserializeDocument(); NJevent("onSwitchDocument"); } } }, _onOpenTextDocument: { value: function(doc) { if(this.activeDocument) { if(this.activeDocument.currentView === "design"){ this.activeDocument.saveAppState(); this.activeDocument.container.parentNode.style["display"] = "none"; this.application.ninja.stage.hideCanvas(true); this.application.ninja.stage.stageView.hideRulers(); } this.activeDocument.container.style["display"] = "none"; } this.application.ninja.stage._scrollFlag = false; // TODO HACK to prevent type error on Hide/Show Iframe this.activeDocument = doc; var type; switch(doc.documentType) { case "css" : type = "css"; break; case "js" : type = "javascript"; break; } DocumentController._codeEditor.editor = CodeMirror.fromTextArea(doc.textArea, { lineNumbers: true, mode: type, onCursorActivity: function() { DocumentController._codeEditor.editor.setLineClass(DocumentController._codeEditor.hline, null); DocumentController._codeEditor.hline = DocumentController._codeEditor.editor.setLineClass(DocumentController._codeEditor.editor.getCursor().line, "activeline"); } }); DocumentController._codeEditor.hline = DocumentController._codeEditor.editor.setLineClass(0, "activeline"); } }, /** * VIEW Related Methods */ // PRIVATE _findDocumentByUUID: { value: function(uuid) { var len = this._documents.length; for(var i = 0; i < len; i++) { if(this._documents[i].uuid === uuid) return this._documents[i]; } return false; } }, _findIndexByUUID: { value: function(uuid) { var len = this._documents.length; for(var i = 0; i < len; i++) { if(this._documents[i].uuid === uuid) return i; } return false; } }, handleStyleSheetDirty:{ value:function(){ if(!this._hackInitialStyles) { this.activeDocument.model.needsSave = true; } } }, // TODO: Move this into the design views initializeRootStyles: { value: function(documentRoot) { var sc = this.application.ninja.stylesController, styles = {}, needsRule = false, rule; if(sc.getElementStyle(documentRoot, "width", false, false) == null) { styles['width'] = '100%'; needsRule = true; } if(sc.getElementStyle(documentRoot, "height", false, false) == null) { styles['height'] = '100%'; needsRule = true; } if(sc.getElementStyle(documentRoot, "-webkit-transform", false, false) == null) { styles['-webkit-transform'] = 'perspective(1400) matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)'; needsRule = true; } if(sc.getElementStyle(documentRoot, "-webkit-transform-style", false, false) == null) { styles['-webkit-transform-style'] = 'preserve-3d'; needsRule = true; } if(needsRule) { rule = sc.addRule('.ninja-body{}'); sc.setStyles(rule, styles); sc.addClass(documentRoot, "ninja-body"); } } } });