ContentPane.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
  1. if (!dojo._hasResource["dojox.layout.ContentPane"]) { // _hasResource checks
  2. // added by build. Do
  3. // not use _hasResource
  4. // directly in your
  5. // code.
  6. dojo._hasResource["dojox.layout.ContentPane"] = true;
  7. dojo.provide("dojox.layout.ContentPane");
  8. dojo.require("dijit.layout.ContentPane");
  9. (function() { // private scope, sort of a namespace
  10. // TODO: should these methods be moved to dojox.html.cssPathAdjust or
  11. // something?
  12. // css at-rules must be set before any css declarations according to CSS
  13. // spec
  14. // match:
  15. // @import 'http://dojotoolkit.org/dojo.css';
  16. // @import 'you/never/thought/' print;
  17. // @import url("it/would/work") tv, screen;
  18. // @import url(/did/you/now.css);
  19. // but not:
  20. // @namespace dojo "http://dojotoolkit.org/dojo.css"; /* namespace URL
  21. // should always be a absolute URI */
  22. // @charset 'utf-8';
  23. // @media print{ #menuRoot {display:none;} }
  24. // we adjust all paths that dont start on '/' or contains ':'
  25. // (?![a-z]+:|\/)
  26. if (dojo.isIE) {
  27. var alphaImageLoader = /(AlphaImageLoader\([^)]*?src=(['"]))(?![a-z]+:|\/)([^\r\n;}]+?)(\2[^)]*\)\s*[;}]?)/g;
  28. }
  29. var cssPaths = /(?:(?:@import\s*(['"])(?![a-z]+:|\/)([^\r\n;{]+?)\1)|url\(\s*(['"]?)(?![a-z]+:|\/)([^\r\n;]+?)\3\s*\))([a-z, \s]*[;}]?)/g;
  30. function adjustCssPaths(cssUrl, cssText) {
  31. // summary:
  32. // adjusts relative paths in cssText to be relative to cssUrl
  33. // a path is considered relative if it doesn't start with '/' and
  34. // not contains ':'
  35. // description:
  36. // Say we fetch a HTML page from level1/page.html
  37. // It has some inline CSS:
  38. // @import "css/page.css" tv, screen;
  39. // ...
  40. // background-image: url(images/aplhaimage.png);
  41. //
  42. // as we fetched this HTML and therefore this CSS
  43. // from level1/page.html, these paths needs to be adjusted to:
  44. // @import 'level1/css/page.css' tv, screen;
  45. // ...
  46. // background-image: url(level1/images/alphaimage.png);
  47. //
  48. // In IE it will also adjust relative paths in AlphaImageLoader()
  49. // filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='images/alphaimage.png');
  50. // will be adjusted to:
  51. // filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='level1/images/alphaimage.png');
  52. //
  53. // Please note that any relative paths in AlphaImageLoader in
  54. // external css files wont work, as
  55. // the paths in AlphaImageLoader is MUST be declared relative to the
  56. // HTML page,
  57. // not relative to the CSS file that declares it
  58. if (!cssText || !cssUrl) {
  59. return;
  60. }
  61. // support the ImageAlphaFilter if it exists, most people use it in
  62. // IE 6 for transparent PNGs
  63. // We are NOT going to kill it in IE 7 just because the PNGs work
  64. // there. Somebody might have
  65. // other uses for it.
  66. // If user want to disable css filter in IE6 he/she should
  67. // unset filter in a declaration that just IE 6 doesn't understands
  68. // like * > .myselector { filter:none; }
  69. if (alphaImageLoader) {
  70. cssText = cssText.replace(alphaImageLoader, function(ignore,
  71. pre, delim, url, post) {
  72. return pre
  73. + (new dojo._Url(cssUrl, './' + url)
  74. .toString()) + post;
  75. });
  76. }
  77. return cssText.replace(cssPaths, function(ignore, delimStr, strUrl,
  78. delimUrl, urlUrl, media) {
  79. if (strUrl) {
  80. return '@import "'
  81. + (new dojo._Url(cssUrl, './' + strUrl)
  82. .toString()) + '"' + media;
  83. } else {
  84. return 'url('
  85. + (new dojo._Url(cssUrl, './' + urlUrl)
  86. .toString()) + ')' + media;
  87. }
  88. });
  89. }
  90. // attributepaths one tag can have multiple paths, example:
  91. // <input src="..." style="url(..)"/> or <a style="url(..)" href="..">
  92. // <img
  93. // style='filter:progid...AlphaImageLoader(src="noticeTheSrcHereRunsThroughHtmlSrc")'
  94. // src="img">
  95. var htmlAttrPaths = /(<[a-z][a-z0-9]*\s[^>]*)(?:(href|src)=(['"]?)([^>]*?)\3|style=(['"]?)([^>]*?)\5)([^>]*>)/gi;
  96. function adjustHtmlPaths(htmlUrl, cont) {
  97. var url = htmlUrl || "./";
  98. return cont.replace(htmlAttrPaths, function(tag, start, name,
  99. delim, relUrl, delim2, cssText, end) {
  100. return start
  101. + (name
  102. ? (name
  103. + '='
  104. + delim
  105. + (new dojo._Url(url, relUrl)
  106. .toString()) + delim)
  107. : ('style=' + delim2
  108. + adjustCssPaths(url, cssText) + delim2))
  109. + end;
  110. });
  111. }
  112. function secureForInnerHtml(cont) {
  113. /** ******* remove <!DOCTYPE.. and <title>..</title> tag ********* */
  114. // khtml is picky about dom faults, you can't attach a <style> or
  115. // <title> node as child of body
  116. // must go into head, so we need to cut out those tags
  117. return cont.replace(
  118. /(?:\s*<!DOCTYPE\s[^>]+>|<title[^>]*>[\s\S]*?<\/title>)/ig,
  119. "");
  120. }
  121. function snarfStyles(/* String */cssUrl, /* String */cont, /* Array */
  122. styles) {
  123. /**
  124. * ************** cut out all <style> and <link rel="stylesheet"
  125. * href=".."> *************
  126. */
  127. // also return any attributes from this tag (might be a media
  128. // attribute)
  129. // if cssUrl is set it will adjust paths accordingly
  130. styles.attributes = [];
  131. return cont
  132. .replace(
  133. /(?:<style([^>]*)>([\s\S]*?)<\/style>|<link\s+(?=[^>]*rel=['"]?stylesheet)([^>]*?href=(['"])([^>]*?)\4[^>\/]*)\/?>)/gi,
  134. function(ignore, styleAttr, cssText, linkAttr,
  135. delim, href) {
  136. // trim attribute
  137. var i, attr = (styleAttr || linkAttr || "")
  138. .replace(/^\s*([\s\S]*?)\s*$/i, "$1");
  139. if (cssText) {
  140. i = styles.push(cssUrl ? adjustCssPaths(
  141. cssUrl, cssText) : cssText);
  142. } else {
  143. i = styles.push('@import "' + href + '";')
  144. attr = attr
  145. .replace(
  146. /\s*(?:rel|href)=(['"])?[^\s]*\1\s*/gi,
  147. ""); // remove rel=...
  148. // and href=...
  149. }
  150. if (attr) {
  151. attr = attr.split(/\s+/);// split on both
  152. // "\n", "\t", "
  153. // " etc
  154. var atObj = {}, tmp;
  155. for (var j = 0, e = attr.length; j < e; j++) {
  156. tmp = attr[j].split('=')// split
  157. // name='value'
  158. atObj[tmp[0]] = tmp[1].replace(
  159. /^\s*['"]?([\s\S]*?)['"]?\s*$/,
  160. "$1"); // trim and remove ''
  161. }
  162. styles.attributes[i - 1] = atObj;
  163. }
  164. return ""; // squelsh the <style> or <link>
  165. });
  166. }
  167. function snarfScripts(cont, byRef) {
  168. // summary
  169. // strips out script tags from cont
  170. // invoke with
  171. // byRef = {errBack:function(){/*add your download error code
  172. // here*/, downloadRemote: true(default false)}}
  173. // byRef will have {code: 'jscode'} when this scope leaves
  174. byRef.code = "";
  175. function download(src) {
  176. if (byRef.downloadRemote) {
  177. // console.debug('downloading',src);
  178. dojo.xhrGet({
  179. url : src,
  180. sync : true,
  181. load : function(code) {
  182. byRef.code += code + ";";
  183. },
  184. error : byRef.errBack
  185. });
  186. }
  187. }
  188. // match <script>, <script type="text/..., but not <script
  189. // type="dojo(/method)...
  190. return cont
  191. .replace(
  192. /<script\s*(?![^>]*type=['"]?dojo)(?:[^>]*?(?:src=(['"]?)([^>]*?)\1[^>]*)?)*>([\s\S]*?)<\/script>/gi,
  193. function(ignore, delim, src, code) {
  194. if (src) {
  195. download(src);
  196. } else {
  197. byRef.code += code;
  198. }
  199. return "";
  200. });
  201. }
  202. function evalInGlobal(code, appendNode) {
  203. // we do our own eval here as dojo.eval doesn't eval in global
  204. // crossbrowser
  205. // This work X browser but but it relies on a DOM
  206. // plus it doesn't return anything, thats unrelevant here but not
  207. // for dojo core
  208. appendNode = appendNode || dojo.doc.body;
  209. var n = appendNode.ownerDocument.createElement('script');
  210. n.type = "text/javascript";
  211. appendNode.appendChild(n);
  212. n.text = code; // DOM 1 says this should work
  213. }
  214. /*
  215. * ===== dojox.layout.ContentPane.DeferredHandle = { // cancel: Function
  216. * cancel: function(){ // summary: cancel a in flight download },
  217. *
  218. * addOnLoad: function(func){ // summary: add a callback to the onLoad
  219. * chain // func: Function },
  220. *
  221. * addOnUnload: function(func){ // summary: add a callback to the
  222. * onUnload chain // func: Function } } =====
  223. */
  224. dojo.declare("dojox.layout.ContentPane", dijit.layout.ContentPane, {
  225. // summary:
  226. // An extended version of dijit.layout.ContentPane
  227. // Supports infile scrips and external ones declared by <script
  228. // src=''
  229. // relative path adjustments (content fetched from a different
  230. // folder)
  231. // <style> and <link rel='stylesheet' href='..'> tags,
  232. // css paths inside cssText is adjusted (if you set adjustPaths =
  233. // true)
  234. //
  235. // NOTE that dojo.require in script in the fetched file isn't
  236. // recommended
  237. // Many widgets need to be required at page load to work properly
  238. // adjustPaths: Boolean
  239. // Adjust relative paths in html string content to point to this
  240. // page
  241. // Only usefull if you grab content from a another folder then the
  242. // current one
  243. adjustPaths : false,
  244. // cleanContent: Boolean
  245. // summary:
  246. // cleans content to make it less likly to generate DOM/JS errors.
  247. // description:
  248. // usefull if you send contentpane a complete page, instead of a
  249. // html fragment
  250. // scans for
  251. // style nodes, inserts in Document head
  252. // title Node, remove
  253. // DOCTYPE tag, remove
  254. // <!-- *JS code here* -->
  255. // <![CDATA[ *JS code here* ]]>
  256. cleanContent : false,
  257. // renderStyles: Boolean
  258. // trigger/load styles in the content
  259. renderStyles : false,
  260. // executeScripts: Boolean
  261. // Execute (eval) scripts that is found in the content
  262. executeScripts : true,
  263. // scriptHasHooks: Boolean
  264. // replace keyword '_container_' in scripts with
  265. // 'dijit.byId(this.id)'
  266. // NOTE this name might change in the near future
  267. scriptHasHooks : false,
  268. /*
  269. * ====== // ioMethod: dojo.xhrGet|dojo.xhrPost // reference to the
  270. * method that should grab the content ioMethod: dojo.xhrGet,
  271. * // ioArgs: Object // makes it possible to add custom args to
  272. * xhrGet, like ioArgs.headers['X-myHeader'] = 'true' ioArgs: {},
  273. * // onLoadDeferred: dojo.Deferred // callbackchain will start
  274. * when onLoad occurs onLoadDeferred: new dojo.Deferred(),
  275. * // onUnloadDeferred: dojo.Deferred // callbackchain will start
  276. * when onUnload occurs onUnloadDeferred: new dojo.Deferred(),
  277. *
  278. * setHref: function(url){ // summary: replace current content with
  279. * url's content return ;// dojox.layout.ContentPane.DeferredHandle },
  280. *
  281. * refresh: function(){ summary: force a re-download of content
  282. * return ;// dojox.layout.ContentPane.DeferredHandle },
  283. *
  284. * ======
  285. */
  286. constructor : function() {
  287. // init per instance properties, initializer doesn't work here
  288. // because how things is hooked up in dijit._Widget
  289. this.ioArgs = {};
  290. this.ioMethod = dojo.xhrGet;
  291. this.onLoadDeferred = new dojo.Deferred();
  292. this.onUnloadDeferred = new dojo.Deferred();
  293. },
  294. postCreate : function() {
  295. // override to support loadDeferred
  296. this._setUpDeferreds();
  297. dijit.layout.ContentPane.prototype.postCreate.apply(this,
  298. arguments);
  299. },
  300. onExecError : function(e) {
  301. // summary
  302. // event callback, called on script error or on java handler
  303. // error
  304. // overide and return your own html string if you want a some
  305. // text
  306. // displayed within the ContentPane
  307. },
  308. setContent : function(data) {
  309. // summary: set data as new content, sort of like innerHTML
  310. // data: String|DomNode|NodeList|dojo.NodeList
  311. if (!this._isDownloaded) {
  312. var defObj = this._setUpDeferreds();
  313. }
  314. dijit.layout.ContentPane.prototype.setContent.apply(this,
  315. arguments);
  316. return defObj; // dojox.layout.ContentPane.DeferredHandle
  317. },
  318. cancel : function() {
  319. // summary: cancels a inflight download
  320. if (this._xhrDfd && this._xhrDfd.fired == -1) {
  321. // we are still in flight, which means we should reset our
  322. // DeferredHandle
  323. // otherwise we will trigger onUnLoad chain of the canceled
  324. // content,
  325. // the canceled content have never gotten onLoad so it
  326. // shouldn't get onUnload
  327. this.onUnloadDeferred = null;
  328. }
  329. dijit.layout.ContentPane.prototype.cancel
  330. .apply(this, arguments);
  331. },
  332. _setUpDeferreds : function() {
  333. var _t = this, cancel = function() {
  334. _t.cancel();
  335. }
  336. var onLoad = (_t.onLoadDeferred = new dojo.Deferred());
  337. var onUnload = (_t._nextUnloadDeferred = new dojo.Deferred());
  338. return {
  339. cancel : cancel,
  340. addOnLoad : function(func) {
  341. onLoad.addCallback(func);
  342. },
  343. addOnUnload : function(func) {
  344. onUnload.addCallback(func);
  345. }
  346. };
  347. },
  348. _onLoadHandler : function() {
  349. dijit.layout.ContentPane.prototype._onLoadHandler.apply(this,
  350. arguments);
  351. if (this.onLoadDeferred) {
  352. this.onLoadDeferred.callback(true);
  353. }
  354. },
  355. _onUnloadHandler : function() {
  356. this.isLoaded = false;
  357. this.cancel();// need to cancel so we don't get any inflight
  358. // suprises
  359. if (this.onUnloadDeferred) {
  360. this.onUnloadDeferred.callback(true);
  361. }
  362. dijit.layout.ContentPane.prototype._onUnloadHandler.apply(this,
  363. arguments);
  364. if (this._nextUnloadDeferred) {
  365. this.onUnloadDeferred = this._nextUnloadDeferred;
  366. }
  367. },
  368. _onError : function(type, err) {
  369. dijit.layout.ContentPane.prototype._onError.apply(this,
  370. arguments);
  371. if (this.onLoadDeferred) {
  372. this.onLoadDeferred.errback(err);
  373. }
  374. },
  375. _prepareLoad : function(forceLoad) {
  376. // sets up for a xhrLoad, load is deferred until widget is
  377. // showing
  378. var defObj = this._setUpDeferreds();
  379. dijit.layout.ContentPane.prototype._prepareLoad.apply(this,
  380. arguments);
  381. return defObj;
  382. },
  383. _setContent : function(cont) {
  384. // override dijit.layout.ContentPane._setContent, to enable path
  385. // adjustments
  386. var styles = [];// init vars
  387. if (dojo.isString(cont)) {
  388. if (this.adjustPaths && this.href) {
  389. cont = adjustHtmlPaths(this.href, cont);
  390. }
  391. if (this.cleanContent) {
  392. cont = secureForInnerHtml(cont);
  393. }
  394. if (this.renderStyles || this.cleanContent) {
  395. cont = snarfStyles(this.href, cont, styles);
  396. }
  397. // because of a bug in IE, script tags that is first in html
  398. // hierarchy doesnt make it into the DOM
  399. // when content is innerHTML'ed, so we can't use dojo.query
  400. // to retrieve scripts from DOM
  401. if (this.executeScripts) {
  402. var _t = this, code, byRef = {
  403. downloadRemote : true,
  404. errBack : function(e) {
  405. _t._onError.call(_t, 'Exec',
  406. 'Error downloading remote script in "'
  407. + _t.id + '"', e);
  408. }
  409. };
  410. cont = snarfScripts(cont, byRef);
  411. code = byRef.code;
  412. }
  413. // rationale for this block:
  414. // if containerNode/domNode is a table derivate tag, some
  415. // browsers dont allow innerHTML on those
  416. var node = (this.containerNode || this.domNode), pre = post = '', walk = 0;
  417. switch (name = node.nodeName.toLowerCase()) {
  418. case 'tr' :
  419. pre = '<tr>';
  420. post = '</tr>';
  421. walk += 1;// fallthrough
  422. case 'tbody' :
  423. case 'thead' :// children of THEAD is of same type as
  424. // TBODY
  425. pre = '<tbody>' + pre;
  426. post += '</tbody>';
  427. walk += 1;// falltrough
  428. case 'table' :
  429. pre = '<table>' + pre;
  430. post += '</table>';
  431. walk += 1;
  432. break;
  433. }
  434. if (walk) {
  435. var n = node.ownerDocument.createElement('div');
  436. n.innerHTML = pre + cont + post;
  437. do {
  438. n = n.firstChild;
  439. } while (--walk);
  440. cont = n.childNodes;
  441. }
  442. }
  443. // render the content
  444. dijit.layout.ContentPane.prototype._setContent.call(this, cont);
  445. // clear old stylenodes from the DOM
  446. if (this._styleNodes && this._styleNodes.length) {
  447. while (this._styleNodes.length) {
  448. dojo._destroyElement(this._styleNodes.pop());
  449. }
  450. }
  451. // render new style nodes
  452. if (this.renderStyles && styles && styles.length) {
  453. this._renderStyles(styles);
  454. }
  455. if (this.executeScripts && code) {
  456. if (this.cleanContent) {
  457. // clean JS from html comments and other crap that
  458. // browser
  459. // parser takes care of in a normal page load
  460. code = code.replace(
  461. /(<!--|(?:\/\/)?-->|<!\[CDATA\[|\]\]>)/g, '');
  462. }
  463. if (this.scriptHasHooks) {
  464. // replace _container_ with dijit.byId(this.id)
  465. code = code.replace(/_container_(?!\s*=[^=])/g,
  466. "dijit.byId('" + this.id + "')");
  467. }
  468. try {
  469. evalInGlobal(code, (this.containerNode || this.domNode));
  470. } catch (e) {
  471. this._onError('Exec', 'Error eval script in ' + this.id
  472. + ', ' + e.message, e);
  473. }
  474. }
  475. },
  476. _renderStyles : function(styles) {
  477. // insert css from content into document head
  478. this._styleNodes = [];
  479. var st, att, cssText, doc = this.domNode.ownerDocument;
  480. var head = doc.getElementsByTagName('head')[0];
  481. for (var i = 0, e = styles.length; i < e; i++) {
  482. cssText = styles[i];
  483. att = styles.attributes[i];
  484. st = doc.createElement('style');
  485. st.setAttribute("type", "text/css"); // this is required
  486. // in CSS spec!
  487. for (var x in att) {
  488. st.setAttribute(x, att[x])
  489. }
  490. this._styleNodes.push(st);
  491. head.appendChild(st); // must insert into DOM before
  492. // setting cssText
  493. if (st.styleSheet) { // IE
  494. st.styleSheet.cssText = cssText;
  495. } else { // w3c
  496. st.appendChild(doc.createTextNode(cssText));
  497. }
  498. }
  499. }
  500. });
  501. })();
  502. }