diff options
Diffstat (limited to 'js/document/views/design.js')
-rwxr-xr-x | js/document/views/design.js | 273 |
1 files changed, 268 insertions, 5 deletions
diff --git a/js/document/views/design.js b/js/document/views/design.js index 84871257..4f598305 100755 --- a/js/document/views/design.js +++ b/js/document/views/design.js | |||
@@ -7,17 +7,280 @@ No rights, expressed or implied, whatsoever to this software are provided by Mot | |||
7 | //////////////////////////////////////////////////////////////////////// | 7 | //////////////////////////////////////////////////////////////////////// |
8 | // | 8 | // |
9 | var Montage = require("montage/core/core").Montage, | 9 | var Montage = require("montage/core/core").Montage, |
10 | Component = require("montage/ui/component").Component, | 10 | BaseDocumentView = require("js/document/views/base").BaseDocumentView; |
11 | CodeDocumentView = require("js/document/views/code").CodeDocumentView; | ||
12 | //////////////////////////////////////////////////////////////////////// | 11 | //////////////////////////////////////////////////////////////////////// |
13 | // | 12 | // |
14 | exports.DesignDocumentView = Montage.create(CodeDocumentView, { | 13 | exports.DesignDocumentView = Montage.create(BaseDocumentView, { |
15 | //////////////////////////////////////////////////////////////////// | 14 | //////////////////////////////////////////////////////////////////// |
16 | // | 15 | // |
17 | hasTemplate: { | 16 | hasTemplate: { |
18 | enumerable: false, | ||
19 | value: false | 17 | value: false |
20 | } | 18 | }, |
19 | //////////////////////////////////////////////////////////////////// | ||
20 | // | ||
21 | _callback: { | ||
22 | value: null | ||
23 | }, | ||
24 | //////////////////////////////////////////////////////////////////// | ||
25 | // | ||
26 | _document: { | ||
27 | value: null | ||
28 | }, | ||
29 | //////////////////////////////////////////////////////////////////// | ||
30 | // | ||
31 | _headFragment: { | ||
32 | value: null | ||
33 | }, | ||
34 | //////////////////////////////////////////////////////////////////// | ||
35 | // | ||
36 | _observer: { | ||
37 | value: {head: null, body: null} | ||
38 | }, | ||
39 | //////////////////////////////////////////////////////////////////// | ||
40 | // | ||
41 | content: { | ||
42 | value: null | ||
43 | }, | ||
44 | //////////////////////////////////////////////////////////////////// | ||
45 | // | ||
46 | document: { | ||
47 | get: function() {return this._document;}, | ||
48 | set: function(value) {this._document = value;} | ||
49 | }, | ||
50 | //////////////////////////////////////////////////////////////////// | ||
51 | // | ||
52 | initiliaze: { | ||
53 | value: function (parent) { | ||
54 | //Creating iFrame for view | ||
55 | this.iframe = document.createElement("iframe"); | ||
56 | //Setting default styles | ||
57 | this.iframe.style.border = "none"; | ||
58 | this.iframe.style.background = "#FFF"; | ||
59 | this.iframe.style.height = "100%"; | ||
60 | this.iframe.style.width = "100%"; | ||
61 | //Returning reference to iFrame created | ||
62 | return parent.appendChild(this.iframe); | ||
63 | } | ||
64 | }, | ||
65 | //////////////////////////////////////////////////////////////////// | ||
66 | // | ||
67 | render: { | ||
68 | value: function (callback) { | ||
69 | //Storing callback for dispatch ready | ||
70 | this._callback = callback; | ||
71 | //Adding listener to know when template is loaded to then load user content | ||
72 | this.iframe.addEventListener("load", this.onTemplateLoad.bind(this), false); | ||
73 | //TODO: Add source parameter and root (optional) | ||
74 | this.iframe.src = "js/document/templates/montage-web/index.html"; | ||
75 | } | ||
76 | }, | ||
77 | //////////////////////////////////////////////////////////////////// | ||
78 | // | ||
79 | onTemplateLoad: { | ||
80 | value: function (e) { | ||
81 | //Removing event | ||
82 | this.iframe.removeEventListener("load", this.onTemplateLoad.bind(this), false); | ||
83 | //TODO: Improve usage of this reference | ||
84 | this.document = this.iframe.contentWindow.document; | ||
85 | //Looping through template styles and marking them with ninja data attribute for I/O clean up | ||
86 | for (var k in this.document.styleSheets) { | ||
87 | if (this.document.styleSheets[k].ownerNode && this.document.styleSheets[k].ownerNode.setAttribute) { | ||
88 | this.document.styleSheets[k].ownerNode.setAttribute('data-ninja-template', 'true'); | ||
89 | } | ||
90 | } | ||
91 | //Creating temp code fragement to load head | ||
92 | this._headFragment = this.document.createElement('head'); | ||
93 | //Adding event listener to know when head is ready, event only dispatched once when using innerHTML | ||
94 | this._observer.head = new WebKitMutationObserver(this.insertHeadContent.bind(this)); | ||
95 | this._observer.head.observe(this._headFragment, {childList: true}); | ||
96 | //Inserting <head> HTML and parsing URLs via mediator method | ||
97 | this._headFragment.innerHTML = (this.content.head.replace(/\b(href|src)\s*=\s*"([^"]*)"/g, this.application.ninja.ioMediator.getNinjaPropUrlRedirect.bind(this.application.ninja.ioMediator))).replace(/url\(([^"]*)(.+?)\1\)/g, this.application.ninja.ioMediator.getNinjaPropUrlRedirect.bind(this.application.ninja.ioMediator)); | ||
98 | //Adding event listener to know when the body is ready and make callback (using HTML5 new DOM Mutation Events) | ||
99 | this._observer.body = new WebKitMutationObserver(this.bodyContentLoaded.bind(this)); | ||
100 | this._observer.body.observe(this.document.body, {childList: true}); | ||
101 | //Inserting <body> HTML and parsing URLs via mediator method | ||
102 | this.document.body.innerHTML += '<ninjaloadinghack></ninjaloadinghack>'+(this.content.body.replace(/\b(href|src)\s*=\s*"([^"]*)"/g, this.application.ninja.ioMediator.getNinjaPropUrlRedirect.bind(this.application.ninja.ioMediator))).replace(/url\(([^"]*)(.+?)\1\)/g, this.application.ninja.ioMediator.getNinjaPropUrlRedirect.bind(this.application.ninja.ioMediator)); | ||
103 | } | ||
104 | }, | ||
105 | //////////////////////////////////////////////////////////////////// | ||
106 | // | ||
107 | insertHeadContent: { | ||
108 | value: function (e) { | ||
109 | //Removing event | ||
110 | this._observer.head.disconnect(); | ||
111 | this._observer.head = null; | ||
112 | //Adding the loaded nodes from code fragment into actual document head | ||
113 | for(var i in this._headFragment.childNodes) { | ||
114 | //Minor hack to know node is actual HTML node | ||
115 | if(this._headFragment.childNodes[i].outerHTML) { | ||
116 | this.document.head.appendChild(this._headFragment.childNodes[i]); | ||
117 | } | ||
118 | } | ||
119 | } | ||
120 | }, | ||
121 | //////////////////////////////////////////////////////////////////// | ||
122 | // | ||
123 | bodyContentLoaded: { | ||
124 | value: function (e) { | ||
125 | //Removing event, only needed on initial load | ||
126 | this._observer.body.disconnect(); | ||
127 | this._observer.body = null; | ||
128 | //Removing loading container | ||
129 | this.document.body.removeChild(this.document.getElementsByTagName('ninjaloadinghack')[0]); | ||
130 | //Getting style and link tags in document | ||
131 | var stags = this.document.getElementsByTagName('style'), | ||
132 | ltags = this.document.getElementsByTagName('link'); | ||
133 | //Temporarily checking for disabled special case (we must enabled for Ninja to access styles) | ||
134 | this.ninjaDisableAttribute(stags); | ||
135 | this.ninjaDisableAttribute(ltags); | ||
136 | //Looping through all link tags to reload into style tags | ||
137 | if(ltags.length > 0) { | ||
138 | for (var i = 0; i < ltags.length; i++) { | ||
139 | // | ||
140 | if (ltags[i].href) { | ||
141 | //TODO: Verify this works for tags in body as well (working in head) | ||
142 | this.document.head.insertBefore(this.getStyleTagFromCssFile(ltags[i]), ltags[i]) || this.document.body.insertBefore(this.getStyleTagFromCssFile(ltags[i]), ltags[i]); | ||
143 | //Disabling tag once it has been reloaded | ||
144 | ltags[i].setAttribute('disabled', 'true'); | ||
145 | } else { | ||
146 | //Error: TBD | ||
147 | //TODO: Determine what link tags would not have href data and error | ||
148 | } | ||
149 | } | ||
150 | } | ||
151 | |||
152 | //TODO: Load webGL (blocking) | ||
153 | |||
154 | //TODO: Load Montage Components (blocking) | ||
155 | |||
156 | //Makign callback if specified | ||
157 | if (this._callback) this._callback(); | ||
158 | } | ||
159 | }, | ||
160 | //////////////////////////////////////////////////////////////////// | ||
161 | // | ||
162 | ninjaDisableAttribute: { | ||
163 | value: function (tags) { | ||
164 | //Looping through tags | ||
165 | for (var i = 0; i < tags.length; i++) { | ||
166 | if (tags[i].getAttribute('data-ninja-template') === null) { | ||
167 | if (tags[i].getAttribute('disabled')) { | ||
168 | tags[i].removeAttribute('disabled'); | ||
169 | tags[i].setAttribute('data-ninja-disabled', 'true'); | ||
170 | } | ||
171 | } | ||
172 | } | ||
173 | } | ||
174 | }, | ||
175 | //////////////////////////////////////////////////////////////////// | ||
176 | // | ||
177 | getStyleTagFromCssFile: { | ||
178 | value: function (linktag) { | ||
179 | // | ||
180 | var tag, cssUrl, fileUri, cssData, docRootUrl; | ||
181 | //Creating style tag to load CSS content into | ||
182 | tag = this.document.createElement('style'); | ||
183 | tag.setAttribute('type', 'text/css'); | ||
184 | //TODO: Remove usage of hack reference of URL | ||
185 | docRootUrl = this.application.ninja.coreIoApi.rootUrl+escape((this.application.ninja.documentController.documentHackReference.root.split(this.application.ninja.coreIoApi.cloudData.root)[1]).replace(/\/\//gi, '/')); | ||
186 | //Checking for location of href to load (special case for cross-domain) | ||
187 | if (linktag.href.indexOf(this.application.ninja.coreIoApi.rootUrl) !== -1) { | ||
188 | //Getting file URI (not URL since we must load through I/O API) | ||
189 | cssUrl = linktag.href.split(this.application.ninja.coreIoApi.rootUrl)[1]; | ||
190 | fileUri = this.application.ninja.coreIoApi.cloudData.root+cssUrl; | ||
191 | //Loading data from CSS file | ||
192 | cssData = this.application.ninja.coreIoApi.readFile({uri: fileUri}); | ||
193 | //Setting properties of locally loaded styles | ||
194 | tag.setAttribute('data-ninja-uri', fileUri); | ||
195 | tag.setAttribute('data-ninja-file-url', cssUrl); | ||
196 | tag.setAttribute('data-ninja-file-read-only', JSON.parse(this.application.ninja.coreIoApi.isFileWritable({uri: fileUri}).content).readOnly); | ||
197 | tag.setAttribute('data-ninja-file-name', cssUrl.split('/')[cssUrl.split('/').length-1]); | ||
198 | } else { | ||
199 | //Cross-domain content | ||
200 | cssData = this.application.ninja.coreIoApi.readExternalFile({url: linktag.href, binary: false}); | ||
201 | //Setting properties of externally loaded styles | ||
202 | tag.setAttribute('data-ninja-external-url', linktag.href); | ||
203 | tag.setAttribute('data-ninja-file-read-only', "true"); | ||
204 | tag.setAttribute('data-ninja-file-name', linktag.href.split('/')[linktag.href.split('/').length-1]); | ||
205 | } | ||
206 | //Copying attributes to maintain same properties as the <link> | ||
207 | for (var n in linktag.attributes) { | ||
208 | if (linktag.attributes[n].value && linktag.attributes[n].name !== 'disabled') { | ||
209 | if (linktag.attributes[n].value.indexOf(docRootUrl) !== -1) { | ||
210 | tag.setAttribute(linktag.attributes[n].name, linktag.attributes[n].value.split(docRootUrl)[1]); | ||
211 | } else { | ||
212 | tag.setAttribute(linktag.attributes[n].name, linktag.attributes[n].value); | ||
213 | } | ||