1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
|
//>>built
define("dojox/html/_base", [
"dojo/_base/kernel",
"dojo/_base/lang",
"dojo/_base/xhr",
"dojo/_base/window",
"dojo/_base/sniff",
"dojo/_base/url",
"dojo/dom-construct",
"dojo/html",
"dojo/_base/declare"
], function (dojo, lang, xhrUtil, windowUtil, has, _Url, domConstruct, htmlUtil) {
/*
Status: dont know where this will all live exactly
Need to pull in the implementation of the various helper methods
Some can be static method, others maybe methods of the ContentSetter (?)
Gut the ContentPane, replace its _setContent with our own call to dojox.html.set()
*/
var html = dojo.getObject("dojox.html", true);
if(has("ie")){
var alphaImageLoader = /(AlphaImageLoader\([^)]*?src=(['"]))(?![a-z]+:|\/)([^\r\n;}]+?)(\2[^)]*\)\s*[;}]?)/g;
}
// css at-rules must be set before any css declarations according to CSS spec
// match:
// @import 'http://dojotoolkit.org/dojo.css';
// @import 'you/never/thought/' print;
// @import url("it/would/work") tv, screen;
// @import url(/did/you/now.css);
// but not:
// @namespace dojo "http://dojotoolkit.org/dojo.css"; /* namespace URL should always be a absolute URI */
// @charset 'utf-8';
// @media print{ #menuRoot {display:none;} }
// we adjust all paths that dont start on '/' or contains ':'
//(?![a-z]+:|\/)
var cssPaths = /(?:(?:@import\s*(['"])(?![a-z]+:|\/)([^\r\n;{]+?)\1)|url\(\s*(['"]?)(?![a-z]+:|\/)([^\r\n;]+?)\3\s*\))([a-z, \s]*[;}]?)/g;
var adjustCssPaths = html._adjustCssPaths = function(cssUrl, cssText){
// summary:
// adjusts relative paths in cssText to be relative to cssUrl
// a path is considered relative if it doesn't start with '/' and not contains ':'
// description:
// Say we fetch a HTML page from level1/page.html
// It has some inline CSS:
// @import "css/page.css" tv, screen;
// ...
// background-image: url(images/aplhaimage.png);
//
// as we fetched this HTML and therefore this CSS
// from level1/page.html, these paths needs to be adjusted to:
// @import 'level1/css/page.css' tv, screen;
// ...
// background-image: url(level1/images/alphaimage.png);
//
// In IE it will also adjust relative paths in AlphaImageLoader()
// filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='images/alphaimage.png');
// will be adjusted to:
// filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='level1/images/alphaimage.png');
//
// Please note that any relative paths in AlphaImageLoader in external css files wont work, as
// the paths in AlphaImageLoader is MUST be declared relative to the HTML page,
// not relative to the CSS file that declares it
if(!cssText || !cssUrl){ return; }
// support the ImageAlphaFilter if it exists, most people use it in IE 6 for transparent PNGs
// We are NOT going to kill it in IE 7 just because the PNGs work there. Somebody might have
// other uses for it.
// If user want to disable css filter in IE6 he/she should
// unset filter in a declaration that just IE 6 doesn't understands
// like * > .myselector { filter:none; }
if(alphaImageLoader){
cssText = cssText.replace(alphaImageLoader, function(ignore, pre, delim, url, post){
return pre + (new _Url(cssUrl, './'+url).toString()) + post;
});
}
return cssText.replace(cssPaths, function(ignore, delimStr, strUrl, delimUrl, urlUrl, media){
if(strUrl){
return '@import "' + (new _Url(cssUrl, './'+strUrl).toString()) + '"' + media;
}else{
return 'url(' + (new _Url(cssUrl, './'+urlUrl).toString()) + ')' + media;
}
});
};
// attributepaths one tag can have multiple paths, example:
// <input src="..." style="url(..)"/> or <a style="url(..)" href="..">
// <img style='filter:progid...AlphaImageLoader(src="noticeTheSrcHereRunsThroughHtmlSrc")' src="img">
var htmlAttrPaths = /(<[a-z][a-z0-9]*\s[^>]*)(?:(href|src)=(['"]?)([^>]*?)\3|style=(['"]?)([^>]*?)\5)([^>]*>)/gi;
var adjustHtmlPaths = html._adjustHtmlPaths = function(htmlUrl, cont){
var url = htmlUrl || "./";
return cont.replace(htmlAttrPaths,
function(tag, start, name, delim, relUrl, delim2, cssText, end){
return start + (name ?
(name + '=' + delim + (new _Url(url, relUrl).toString()) + delim)
: ('style=' + delim2 + adjustCssPaths(url, cssText) + delim2)
) + end;
}
);
};
var snarfStyles = html._snarfStyles = function (/*String*/cssUrl, /*String*/cont, /*Array*/styles){
/**************** cut out all <style> and <link rel="stylesheet" href=".."> **************/
// also return any attributes from this tag (might be a media attribute)
// if cssUrl is set it will adjust paths accordingly
styles.attributes = [];
return cont.replace(/(?:<style([^>]*)>([\s\S]*?)<\/style>|<link\s+(?=[^>]*rel=['"]?stylesheet)([^>]*?href=(['"])([^>]*?)\4[^>\/]*)\/?>)/gi,
function(ignore, styleAttr, cssText, linkAttr, delim, href){
// trim attribute
var i, attr = (styleAttr||linkAttr||"").replace(/^\s*([\s\S]*?)\s*$/i, "$1");
if(cssText){
i = styles.push(cssUrl ? adjustCssPaths(cssUrl, cssText) : cssText);
}else{
i = styles.push('@import "' + href + '";');
attr = attr.replace(/\s*(?:rel|href)=(['"])?[^\s]*\1\s*/gi, ""); // remove rel=... and href=...
}
if(attr){
attr = attr.split(/\s+/);// split on both "\n", "\t", " " etc
var atObj = {}, tmp;
for(var j = 0, e = attr.length; j < e; j++){
tmp = attr[j].split('='); // split name='value'
atObj[tmp[0]] = tmp[1].replace(/^\s*['"]?([\s\S]*?)['"]?\s*$/, "$1"); // trim and remove ''
}
styles.attributes[i - 1] = atObj;
}
return "";
}
);
};
var snarfScripts = html._snarfScripts = function(cont, byRef){
// summary
// strips out script tags from cont
// invoke with
// byRef = {errBack:function(){/*add your download error code here*/, downloadRemote: true(default false)}}
// byRef will have {code: 'jscode'} when this scope leaves
byRef.code = "";
//Update script tags nested in comments so that the script tag collector doesn't pick
//them up.
cont = cont.replace(/<[!][-][-](.|\s)*?[-][-]>/g,
function(comment){
return comment.replace(/<(\/?)script\b/ig,"<$1Script");
}
);
function download(src){
if(byRef.downloadRemote){
// console.debug('downloading',src);
//Fix up src, in case there were entity character encodings in it.
//Probably only need to worry about a subset.
src = src.replace(/&([a-z0-9#]+);/g, function(m, name) {
switch(name) {
case "amp" : return "&";
case "gt" : return ">";
case "lt" : return "<";
default:
return name.charAt(0)=="#" ? String.fromCharCode(name.substring(1)) : "&"+name+";";
}
});
xhrUtil.get({
url: src,
sync: true,
load: function(code){
byRef.code += code+";";
},
error: byRef.errBack
});
}
}
// match <script>, <script type="text/..., but not <script type="dojo(/method)...
return cont.replace(/<script\s*(?![^>]*type=['"]?(?:dojo\/|text\/html\b))(?:[^>]*?(?:src=(['"]?)([^>]*?)\1[^>]*)?)*>([\s\S]*?)<\/script>/gi,
function(ignore, delim, src, code){
if(src){
download(src);
}else{
byRef.code += code;
}
return "";
}
);
};
var evalInGlobal = html.evalInGlobal = function(code, appendNode){
// we do our own eval here as dojo.eval doesn't eval in global crossbrowser
// This work X browser but but it relies on a DOM
// plus it doesn't return anything, thats unrelevant here but not for dojo core
appendNode = appendNode || windowUtil.doc.body;
var n = appendNode.ownerDocument.createElement('script');
n.type = "text/javascript";
appendNode.appendChild(n);
n.text = code; // DOM 1 says this should work
};
html._ContentSetter = dojo.declare(/*===== "dojox.html._ContentSetter", =====*/ htmlUtil._ContentSetter, {
// adjustPaths: Boolean
// Adjust relative paths in html string content to point to this page
// Only useful if you grab content from a another folder than the current one
adjustPaths: false,
referencePath: ".",
renderStyles: false,
executeScripts: false,
scriptHasHooks: false,
scriptHookReplacement: null,
_renderStyles: function(styles){
// insert css from content into document head
this._styleNodes = [];
var st, att, cssText, doc = this.node.ownerDocument;
var head = doc.getElementsByTagName('head')[0];
for(var i = 0, e = styles.length; i < e; i++){
cssText = styles[i]; att = styles.attributes[i];
st = doc.createElement('style');
st.setAttribute("type", "text/css"); // this is required in CSS spec!
for(var x in att){
st.setAttribute(x, att[x]);
}
this._styleNodes.push(st);
head.appendChild(st); // must insert into DOM before setting cssText
if(st.styleSheet){ // IE
st.styleSheet.cssText = cssText;
}else{ // w3c
st.appendChild(doc.createTextNode(cssText));
}
}
},
empty: function() {
this.inherited("empty", arguments);
// empty out the styles array from any previous use
this._styles = [];
},
onBegin: function() {
// summary
// Called after instantiation, but before set();
// It allows modification of any of the object properties - including the node and content
// provided - before the set operation actually takes place
// This implementation extends that of dojo.html._ContentSetter
// to add handling for adjustPaths, renderStyles on the html string content before it is set
this.inherited("onBegin", arguments);
var cont = this.content,
node = this.node;
var styles = this._styles;// init vars
if(lang.isString(cont)){
if(this.adjustPaths && this.referencePath){
cont = adjustHtmlPaths(this.referencePath, cont);
}
if(this.renderStyles || this.cleanContent){
cont = snarfStyles(this.referencePath, cont, styles);
}
// because of a bug in IE, script tags that is first in html hierarchy doesnt make it into the DOM
// when content is innerHTML'ed, so we can't use dojo.query to retrieve scripts from DOM
if(this.executeScripts){
var _t = this;
var byRef = {
downloadRemote: true,
errBack:function(e){
_t._onError.call(_t, 'Exec', 'Error downloading remote script in "'+_t.id+'"', e);
}
};
cont = snarfScripts(cont, byRef);
this._code = byRef.code;
}
}
this.content = cont;
},
onEnd: function() {
// summary
// Called after set(), when the new content has been pushed into the node
// It provides an opportunity for post-processing before handing back the node to the caller
// This implementation extends that of dojo.html._ContentSetter
var code = this._code,
styles = this._styles;
// clear old stylenodes from the DOM
// these were added by the last set call
// (in other words, if you dont keep and reuse the ContentSetter for a particular node
// .. you'll have no practical way to do this)
if(this._styleNodes && this._styleNodes.length){
while(this._styleNodes.length){
domConstruct.destroy(this._styleNodes.pop());
}
}
// render new style nodes
if(this.renderStyles && styles && styles.length){
this._renderStyles(styles);
}
if(this.executeScripts && code){
if(this.cleanContent){
// clean JS from html comments and other crap that browser
// parser takes care of in a normal page load
code = code.replace(/(<!--|(?:\/\/)?-->|<!\[CDATA\[|\]\]>)/g, '');
}
if(this.scriptHasHooks){
// replace _container_ with this.scriptHookReplace()
// the scriptHookReplacement can be a string
// or a function, which when invoked returns the string you want to substitute in
code = code.replace(/_container_(?!\s*=[^=])/g, this.scriptHookReplacement);
}
try{
evalInGlobal(code, this.node);
}catch(e){
this._onError('Exec', 'Error eval script in '+this.id+', '+e.message, e);
}
}
this.inherited("onEnd", arguments);
},
tearDown: function() {
this.inherited(arguments);
delete this._styles;
// only tear down -or another set() - will explicitly throw away the
// references to the style nodes we added
if(this._styleNodes && this._styleNodes.length){
while(this._styleNodes.length){
domConstruct.destroy(this._styleNodes.pop());
}
}
delete this._styleNodes;
// reset the defaults from the prototype
// XXX: not sure if this is the correct intended behaviour, it was originally
// dojo.getObject(this.declaredClass).prototype which will not work with anonymous
// modules
dojo.mixin(this, html._ContentSetter.prototype);
}
});
html.set = function(/* DomNode */ node, /* String|DomNode|NodeList */ cont, /* Object? */ params){
// TODO: add all the other options
// summary:
// inserts (replaces) the given content into the given node
// node:
// the parent element that will receive the content
// cont:
// the content to be set on the parent element.
// This can be an html string, a node reference or a NodeList, dojo.NodeList, Array or other enumerable list of nodes
// params:
// Optional flags/properties to configure the content-setting. See dojo.html._ContentSetter
// example:
// A safe string/node/nodelist content replacement/injection with hooks for extension
// Example Usage:
// dojo.html.set(node, "some string");
// dojo.html.set(node, contentNode, {options});
// dojo.html.set(node, myNode.childNodes, {options});
if(!params){
// simple and fast
return htmlUtil._setNodeContent(node, cont, true);
}else{
// more options but slower
var op = new html._ContentSetter(dojo.mixin(
params,
{ content: cont, node: node }
));
return op.set();
}
};
return html;
});
|