Dashboard sipadu mbip
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

frame-debug.js 37KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023
  1. /*
  2. Copyright (c) 2010, Yahoo! Inc. All rights reserved.
  3. Code licensed under the BSD License:
  4. http://developer.yahoo.com/yui/license.html
  5. version: 3.4.0
  6. build: nightly
  7. */
  8. YUI.add('frame', function(Y) {
  9. /**
  10. * Creates a wrapper around an iframe. It loads the content either from a local
  11. * file or from script and creates a local YUI instance bound to that new window and document.
  12. * @class Frame
  13. * @for Frame
  14. * @extends Base
  15. * @constructor
  16. * @module editor
  17. * @submodule frame
  18. */
  19. var Frame = function() {
  20. Frame.superclass.constructor.apply(this, arguments);
  21. }, LAST_CHILD = ':last-child', BODY = 'body';
  22. Y.extend(Frame, Y.Base, {
  23. /**
  24. * @private
  25. * @property _ready
  26. * @description Internal reference set when the content is ready.
  27. * @type Boolean
  28. */
  29. _ready: null,
  30. /**
  31. * @private
  32. * @property _rendered
  33. * @description Internal reference set when render is called.
  34. * @type Boolean
  35. */
  36. _rendered: null,
  37. /**
  38. * @private
  39. * @property _iframe
  40. * @description Internal Node reference to the iFrame or the window
  41. * @type Node
  42. */
  43. _iframe: null,
  44. /**
  45. * @private
  46. * @property _instance
  47. * @description Internal reference to the YUI instance bound to the iFrame or window
  48. * @type YUI
  49. */
  50. _instance: null,
  51. /**
  52. * @private
  53. * @method _create
  54. * @description Create the iframe or Window and get references to the Document & Window
  55. * @return {Object} Hash table containing references to the new Document & Window
  56. */
  57. _create: function(cb) {
  58. var win, doc, res, node;
  59. this._iframe = Y.Node.create(Frame.HTML);
  60. this._iframe.setStyle('visibility', 'hidden');
  61. this._iframe.set('src', this.get('src'));
  62. this.get('container').append(this._iframe);
  63. this._iframe.set('height', '99%');
  64. var html = '',
  65. extra_css = ((this.get('extracss')) ? '<style id="extra_css">' + this.get('extracss') + '</style>' : '');
  66. Y.log('Creating the document from javascript', 'info', 'frame');
  67. html = Y.substitute(Frame.PAGE_HTML, {
  68. DIR: this.get('dir'),
  69. LANG: this.get('lang'),
  70. TITLE: this.get('title'),
  71. META: Frame.META,
  72. LINKED_CSS: this.get('linkedcss'),
  73. CONTENT: this.get('content'),
  74. BASE_HREF: this.get('basehref'),
  75. DEFAULT_CSS: Frame.DEFAULT_CSS,
  76. EXTRA_CSS: extra_css
  77. });
  78. if (Y.config.doc.compatMode != 'BackCompat') {
  79. Y.log('Adding Doctype to frame: ' + Frame.getDocType(), 'info', 'frame');
  80. //html = Frame.DOC_TYPE + "\n" + html;
  81. html = Frame.getDocType() + "\n" + html;
  82. } else {
  83. Y.log('DocType skipped because we are in BackCompat Mode.', 'warn', 'frame');
  84. }
  85. Y.log('Injecting content into iframe', 'info', 'frame');
  86. res = this._resolveWinDoc();
  87. res.doc.open();
  88. res.doc.write(html);
  89. res.doc.close();
  90. if (!res.doc.documentElement) {
  91. Y.log('document.documentElement was not found, running timer', 'warn', 'frame');
  92. var timer = Y.later(1, this, function() {
  93. if (res.doc && res.doc.documentElement) {
  94. Y.log('document.documentElement found inside timer', 'info', 'frame');
  95. cb(res);
  96. timer.cancel();
  97. }
  98. }, null, true);
  99. } else {
  100. Y.log('document.documentElement found', 'info', 'frame');
  101. cb(res);
  102. }
  103. },
  104. /**
  105. * @private
  106. * @method _resolveWinDoc
  107. * @description Resolves the document and window from an iframe or window instance
  108. * @param {Object} c The YUI Config to add the window and document to
  109. * @return {Object} Object hash of window and document references, if a YUI config was passed, it is returned.
  110. */
  111. _resolveWinDoc: function(c) {
  112. var config = (c) ? c : {};
  113. config.win = Y.Node.getDOMNode(this._iframe.get('contentWindow'));
  114. config.doc = Y.Node.getDOMNode(this._iframe.get('contentWindow.document'));
  115. if (!config.doc) {
  116. config.doc = Y.config.doc;
  117. }
  118. if (!config.win) {
  119. config.win = Y.config.win;
  120. }
  121. return config;
  122. },
  123. /**
  124. * @private
  125. * @method _onDomEvent
  126. * @description Generic handler for all DOM events fired by the iframe or window. This handler
  127. * takes the current EventFacade and augments it to fire on the Frame host. It adds two new properties
  128. * to the EventFacade called frameX and frameY which adds the scroll and xy position of the iframe
  129. * to the original pageX and pageY of the event so external nodes can be positioned over the frame.
  130. * @param {Event.Facade} e
  131. */
  132. _onDomEvent: function(e) {
  133. var xy, node;
  134. if (!Y.Node.getDOMNode(this._iframe)) {
  135. //The iframe is null for some reason, bail on sending events.
  136. return;
  137. }
  138. //Y.log('onDOMEvent: ' + e.type, 'info', 'frame');
  139. e.frameX = e.frameY = 0;
  140. if (e.pageX > 0 || e.pageY > 0) {
  141. if (e.type.substring(0, 3) !== 'key') {
  142. node = this._instance.one('win');
  143. xy = this._iframe.getXY();
  144. e.frameX = xy[0] + e.pageX - node.get('scrollLeft');
  145. e.frameY = xy[1] + e.pageY - node.get('scrollTop');
  146. }
  147. }
  148. e.frameTarget = e.target;
  149. e.frameCurrentTarget = e.currentTarget;
  150. e.frameEvent = e;
  151. this.fire('dom:' + e.type, e);
  152. },
  153. initializer: function() {
  154. this.publish('ready', {
  155. emitFacade: true,
  156. defaultFn: this._defReadyFn
  157. });
  158. },
  159. destructor: function() {
  160. var inst = this.getInstance();
  161. inst.one('doc').detachAll();
  162. inst = null;
  163. this._iframe.remove();
  164. },
  165. /**
  166. * @private
  167. * @method _DOMPaste
  168. * @description Simple pass thru handler for the paste event so we can do content cleanup
  169. * @param {Event.Facade} e
  170. */
  171. _DOMPaste: function(e) {
  172. var inst = this.getInstance(),
  173. data = '', win = inst.config.win;
  174. if (e._event.originalTarget) {
  175. data = e._event.originalTarget;
  176. }
  177. if (e._event.clipboardData) {
  178. data = e._event.clipboardData.getData('Text');
  179. }
  180. if (win.clipboardData) {
  181. data = win.clipboardData.getData('Text');
  182. if (data === '') { // Could be empty, or failed
  183. // Verify failure
  184. if (!win.clipboardData.setData('Text', data)) {
  185. data = null;
  186. }
  187. }
  188. }
  189. e.frameTarget = e.target;
  190. e.frameCurrentTarget = e.currentTarget;
  191. e.frameEvent = e;
  192. if (data) {
  193. e.clipboardData = {
  194. data: data,
  195. getData: function() {
  196. return data;
  197. }
  198. };
  199. } else {
  200. Y.log('Failed to collect clipboard data', 'warn', 'frame');
  201. e.clipboardData = null;
  202. }
  203. this.fire('dom:paste', e);
  204. },
  205. /**
  206. * @private
  207. * @method _defReadyFn
  208. * @description Binds DOM events, sets the iframe to visible and fires the ready event
  209. */
  210. _defReadyFn: function() {
  211. var inst = this.getInstance();
  212. Y.each(Frame.DOM_EVENTS, function(v, k) {
  213. var fn = Y.bind(this._onDomEvent, this),
  214. kfn = ((Y.UA.ie && Frame.THROTTLE_TIME > 0) ? Y.throttle(fn, Frame.THROTTLE_TIME) : fn);
  215. if (!inst.Node.DOM_EVENTS[k]) {
  216. inst.Node.DOM_EVENTS[k] = 1;
  217. }
  218. if (v === 1) {
  219. if (k !== 'focus' && k !== 'blur' && k !== 'paste') {
  220. //Y.log('Adding DOM event to frame: ' + k, 'info', 'frame');
  221. if (k.substring(0, 3) === 'key') {
  222. //Throttle key events in IE
  223. inst.on(k, kfn, inst.config.doc);
  224. } else {
  225. inst.on(k, fn, inst.config.doc);
  226. }
  227. }
  228. }
  229. }, this);
  230. inst.Node.DOM_EVENTS.paste = 1;
  231. inst.on('paste', Y.bind(this._DOMPaste, this), inst.one('body'));
  232. //Adding focus/blur to the window object
  233. inst.on('focus', Y.bind(this._onDomEvent, this), inst.config.win);
  234. inst.on('blur', Y.bind(this._onDomEvent, this), inst.config.win);
  235. inst._use = inst.use;
  236. inst.use = Y.bind(this.use, this);
  237. this._iframe.setStyles({
  238. visibility: 'inherit'
  239. });
  240. inst.one('body').setStyle('display', 'block');
  241. if (Y.UA.ie) {
  242. this._fixIECursors();
  243. }
  244. },
  245. /**
  246. * It appears that having a BR tag anywhere in the source "below" a table with a percentage width (in IE 7 & 8)
  247. * if there is any TEXTINPUT's outside the iframe, the cursor will rapidly flickr and the CPU would occasionally
  248. * spike. This method finds all <BR>'s below the sourceIndex of the first table. Does some checks to see if they
  249. * can be modified and replaces then with a <WBR> so the layout will remain in tact, but the flickering will
  250. * no longer happen.
  251. * @method _fixIECursors
  252. * @private
  253. */
  254. _fixIECursors: function() {
  255. var inst = this.getInstance(),
  256. tables = inst.all('table'),
  257. brs = inst.all('br'), si;
  258. if (tables.size() && brs.size()) {
  259. //First Table
  260. si = tables.item(0).get('sourceIndex');
  261. brs.each(function(n) {
  262. var p = n.get('parentNode'),
  263. c = p.get('children'), b = p.all('>br');
  264. if (p.test('div')) {
  265. if (c.size() > 2) {
  266. n.replace(inst.Node.create('<wbr>'));
  267. } else {
  268. if (n.get('sourceIndex') > si) {
  269. if (b.size()) {
  270. n.replace(inst.Node.create('<wbr>'));
  271. }
  272. } else {
  273. if (b.size() > 1) {
  274. n.replace(inst.Node.create('<wbr>'));
  275. }
  276. }
  277. }
  278. }
  279. });
  280. }
  281. },
  282. /**
  283. * @private
  284. * @method _onContentReady
  285. * @description Called once the content is available in the frame/window and calls the final use call
  286. * on the internal instance so that the modules are loaded properly.
  287. */
  288. _onContentReady: function(e) {
  289. if (!this._ready) {
  290. this._ready = true;
  291. var inst = this.getInstance(),
  292. args = Y.clone(this.get('use'));
  293. this.fire('contentready');
  294. Y.log('On available for body of iframe', 'info', 'frame');
  295. if (e) {
  296. inst.config.doc = Y.Node.getDOMNode(e.target);
  297. }
  298. //TODO Circle around and deal with CSS loading...
  299. args.push(Y.bind(function() {
  300. Y.log('Callback from final internal use call', 'info', 'frame');
  301. if (inst.Selection) {
  302. inst.Selection.DEFAULT_BLOCK_TAG = this.get('defaultblock');
  303. }
  304. //Moved to here so that the iframe is ready before allowing editing..
  305. if (this.get('designMode')) {
  306. if(Y.UA.ie) {
  307. inst.config.doc.body.contentEditable = 'true';
  308. this._ieSetBodyHeight();
  309. inst.on('keyup', Y.bind(this._ieSetBodyHeight, this), inst.config.doc);
  310. } else {
  311. inst.config.doc.designMode = 'on';
  312. }
  313. }
  314. this.fire('ready');
  315. }, this));
  316. Y.log('Calling use on internal instance: ' + args, 'info', 'frame');
  317. inst.use.apply(inst, args);
  318. inst.one('doc').get('documentElement').addClass('yui-js-enabled');
  319. }
  320. },
  321. _ieHeightCounter: null,
  322. /**
  323. * Internal method to set the height of the body to the height of the document in IE.
  324. * With contenteditable being set, the document becomes unresponsive to clicks, this
  325. * method expands the body to be the height of the document so that doesn't happen.
  326. * @private
  327. * @method _ieSetBodyHeight
  328. */
  329. _ieSetBodyHeight: function(e) {
  330. if (!this._ieHeightCounter) {
  331. this._ieHeightCounter = 0;
  332. }
  333. this._ieHeightCounter++;
  334. var run = false;
  335. if (!e) {
  336. run = true;
  337. }
  338. if (e) {
  339. switch (e.keyCode) {
  340. case 8:
  341. case 13:
  342. run = true;
  343. break;
  344. }
  345. if (e.ctrlKey || e.shiftKey) {
  346. run = true;
  347. }
  348. }
  349. if (run) {
  350. try {
  351. var inst = this.getInstance();
  352. var h = this._iframe.get('offsetHeight');
  353. var bh = inst.config.doc.body.scrollHeight;
  354. if (h > bh) {
  355. h = (h - 15) + 'px';
  356. inst.config.doc.body.style.height = h;
  357. } else {
  358. inst.config.doc.body.style.height = 'auto';
  359. }
  360. } catch (e) {
  361. if (this._ieHeightCounter < 100) {
  362. Y.later(200, this, this._ieSetBodyHeight);
  363. } else {
  364. Y.log('Failed to set body height in IE', 'error', 'frame');
  365. }
  366. }
  367. }
  368. },
  369. /**
  370. * @private
  371. * @method _resolveBaseHref
  372. * @description Resolves the basehref of the page the frame is created on. Only applies to dynamic content.
  373. * @param {String} href The new value to use, if empty it will be resolved from the current url.
  374. * @return {String}
  375. */
  376. _resolveBaseHref: function(href) {
  377. if (!href || href === '') {
  378. href = Y.config.doc.location.href;
  379. if (href.indexOf('?') !== -1) { //Remove the query string
  380. href = href.substring(0, href.indexOf('?'));
  381. }
  382. href = href.substring(0, href.lastIndexOf('/')) + '/';
  383. }
  384. return href;
  385. },
  386. /**
  387. * @private
  388. * @method _getHTML
  389. * @description Get the content from the iframe
  390. * @param {String} html The raw HTML from the body of the iframe.
  391. * @return {String}
  392. */
  393. _getHTML: function(html) {
  394. if (this._ready) {
  395. var inst = this.getInstance();
  396. html = inst.one('body').get('innerHTML');
  397. }
  398. return html;
  399. },
  400. /**
  401. * @private
  402. * @method _setHTML
  403. * @description Set the content of the iframe
  404. * @param {String} html The raw HTML to set the body of the iframe to.
  405. * @return {String}
  406. */
  407. _setHTML: function(html) {
  408. if (this._ready) {
  409. var inst = this.getInstance();
  410. inst.one('body').set('innerHTML', html);
  411. } else {
  412. //This needs to be wrapped in a contentready callback for the !_ready state
  413. this.on('contentready', Y.bind(function(html, e) {
  414. var inst = this.getInstance();
  415. inst.one('body').set('innerHTML', html);
  416. }, this, html));
  417. }
  418. return html;
  419. },
  420. /**
  421. * @private
  422. * @method _setLinkedCSS
  423. * @description Set's the linked CSS on the instance..
  424. */
  425. _getLinkedCSS: function(urls) {
  426. if (!Y.Lang.isArray(urls)) {
  427. urls = [urls];
  428. }
  429. var str = '';
  430. if (!this._ready) {
  431. Y.each(urls, function(v) {
  432. if (v !== '') {
  433. str += '<link rel="stylesheet" href="' + v + '" type="text/css">';
  434. }
  435. });
  436. } else {
  437. str = urls;
  438. }
  439. return str;
  440. },
  441. /**
  442. * @private
  443. * @method _setLinkedCSS
  444. * @description Set's the linked CSS on the instance..
  445. */
  446. _setLinkedCSS: function(css) {
  447. if (this._ready) {
  448. var inst = this.getInstance();
  449. inst.Get.css(css);
  450. }
  451. return css;
  452. },
  453. /**
  454. * @private
  455. * @method _setExtraCSS
  456. * @description Set's the extra CSS on the instance..
  457. */
  458. _setExtraCSS: function(css) {
  459. if (this._ready) {
  460. var inst = this.getInstance(),
  461. node = inst.one('#extra_css');
  462. node.remove();
  463. inst.one('head').append('<style id="extra_css">' + css + '</style>');
  464. }
  465. return css;
  466. },
  467. /**
  468. * @private
  469. * @method _instanceLoaded
  470. * @description Called from the first YUI instance that sets up the internal instance.
  471. * This loads the content into the window/frame and attaches the contentready event.
  472. * @param {YUI} inst The internal YUI instance bound to the frame/window
  473. */
  474. _instanceLoaded: function(inst) {
  475. this._instance = inst;
  476. this._onContentReady();
  477. var doc = this._instance.config.doc;
  478. if (this.get('designMode')) {
  479. if (!Y.UA.ie) {
  480. try {
  481. //Force other browsers into non CSS styling
  482. doc.execCommand('styleWithCSS', false, false);
  483. doc.execCommand('insertbronreturn', false, false);
  484. } catch (err) {}
  485. }
  486. }
  487. },
  488. //BEGIN PUBLIC METHODS
  489. /**
  490. * @method use
  491. * @description This is a scoped version of the normal YUI.use method & is bound to this frame/window.
  492. * At setup, the inst.use method is mapped to this method.
  493. */
  494. use: function() {
  495. Y.log('Calling augmented use after ready', 'info', 'frame');
  496. var inst = this.getInstance(),
  497. args = Y.Array(arguments),
  498. cb = false;
  499. if (Y.Lang.isFunction(args[args.length - 1])) {
  500. cb = args.pop();
  501. }
  502. if (cb) {
  503. args.push(function() {
  504. Y.log('Internal callback from augmented use', 'info', 'frame');
  505. cb.apply(inst, arguments);
  506. });
  507. }
  508. inst._use.apply(inst, args);
  509. },
  510. /**
  511. * @method delegate
  512. * @description A delegate method passed to the instance's delegate method
  513. * @param {String} type The type of event to listen for
  514. * @param {Function} fn The method to attach
  515. * @param {String} cont The container to act as a delegate, if no "sel" passed, the body is assumed as the container.
  516. * @param {String} sel The selector to match in the event (optional)
  517. * @return {EventHandle} The Event handle returned from Y.delegate
  518. */
  519. delegate: function(type, fn, cont, sel) {
  520. var inst = this.getInstance();
  521. if (!inst) {
  522. Y.log('Delegate events can not be attached until after the ready event has fired.', 'error', 'iframe');
  523. return false;
  524. }
  525. if (!sel) {
  526. sel = cont;
  527. cont = 'body';
  528. }
  529. return inst.delegate(type, fn, cont, sel);
  530. },
  531. /**
  532. * @method getInstance
  533. * @description Get a reference to the internal YUI instance.
  534. * @return {YUI} The internal YUI instance
  535. */
  536. getInstance: function() {
  537. return this._instance;
  538. },
  539. /**
  540. * @method render
  541. * @description Render the iframe into the container config option or open the window.
  542. * @param {String/HTMLElement/Node} node The node to render to
  543. * @return {Frame}
  544. * @chainable
  545. */
  546. render: function(node) {
  547. if (this._rendered) {
  548. Y.log('Frame already rendered.', 'warn', 'frame');
  549. return this;
  550. }
  551. this._rendered = true;
  552. if (node) {
  553. this.set('container', node);
  554. }
  555. this._create(Y.bind(function(res) {
  556. var inst, timer,
  557. cb = Y.bind(function(i) {
  558. Y.log('Internal instance loaded with node-base', 'info', 'frame');
  559. this._instanceLoaded(i);
  560. }, this),
  561. args = Y.clone(this.get('use')),
  562. config = {
  563. debug: false,
  564. win: res.win,
  565. doc: res.doc
  566. },
  567. fn = Y.bind(function() {
  568. Y.log('New Modules Loaded into main instance', 'info', 'frame');
  569. config = this._resolveWinDoc(config);
  570. inst = YUI(config);
  571. inst.host = this.get('host'); //Cross reference to Editor
  572. inst.log = Y.log; //Dump the instance logs to the parent instance.
  573. Y.log('Creating new internal instance with node-base only', 'info', 'frame');
  574. try {
  575. inst.use('node-base', cb);
  576. if (timer) {
  577. clearInterval(timer);
  578. }
  579. } catch (e) {
  580. timer = setInterval(function() {
  581. Y.log('[TIMER] Internal use call failed, retrying', 'info', 'frame');
  582. fn();
  583. }, 350);
  584. Y.log('Internal use call failed, retrying', 'info', 'frame');
  585. }
  586. }, this);
  587. args.push(fn);
  588. Y.log('Adding new modules to main instance: ' + args, 'info', 'frame');
  589. Y.use.apply(Y, args);
  590. }, this));
  591. return this;
  592. },
  593. /**
  594. * @private
  595. * @method _handleFocus
  596. * @description Does some tricks on focus to set the proper cursor position.
  597. */
  598. _handleFocus: function() {
  599. var inst = this.getInstance(),
  600. sel = new inst.Selection();
  601. if (sel.anchorNode) {
  602. Y.log('_handleFocus being called..', 'info', 'frame');
  603. var n = sel.anchorNode, c;
  604. if (n.test('p') && n.get('innerHTML') === '') {
  605. n = n.get('parentNode');
  606. }
  607. c = n.get('childNodes');
  608. if (c.size()) {
  609. if (c.item(0).test('br')) {
  610. sel.selectNode(n, true, false);
  611. } else if (c.item(0).test('p')) {
  612. n = c.item(0).one('br.yui-cursor');
  613. if (n) {
  614. n = n.get('parentNode');
  615. }
  616. if (!n) {
  617. n = c.item(0).get('firstChild');
  618. }
  619. if (!n) {
  620. n = c.item(0);
  621. }
  622. if (n) {
  623. sel.selectNode(n, true, false);
  624. }
  625. } else {
  626. var b = inst.one('br.yui-cursor');
  627. if (b) {
  628. var par = b.get('parentNode');
  629. if (par) {
  630. sel.selectNode(par, true, false);
  631. }
  632. }
  633. }
  634. }
  635. }
  636. },
  637. /**
  638. * @method focus
  639. * @description Set the focus to the iframe
  640. * @param {Function} fn Callback function to execute after focus happens
  641. * @return {Frame}
  642. * @chainable
  643. */
  644. focus: function(fn) {
  645. if (Y.UA.ie && Y.UA.ie < 9) {
  646. try {
  647. Y.one('win').focus();
  648. this.getInstance().one('win').focus();
  649. } catch (ierr) {
  650. Y.log('Frame focus failed', 'warn', 'frame');
  651. }
  652. if (fn === true) {
  653. this._handleFocus();
  654. }
  655. if (Y.Lang.isFunction(fn)) {
  656. fn();
  657. }
  658. } else {
  659. try {
  660. Y.one('win').focus();
  661. Y.later(100, this, function() {
  662. this.getInstance().one('win').focus();
  663. if (fn === true) {
  664. this._handleFocus();
  665. }
  666. if (Y.Lang.isFunction(fn)) {
  667. fn();
  668. }
  669. });
  670. } catch (ferr) {
  671. Y.log('Frame focus failed', 'warn', 'frame');
  672. }
  673. }
  674. return this;
  675. },
  676. /**
  677. * @method show
  678. * @description Show the iframe instance
  679. * @return {Frame}
  680. * @chainable
  681. */
  682. show: function() {
  683. this._iframe.setStyles({
  684. position: 'static',
  685. left: ''
  686. });
  687. if (Y.UA.gecko) {
  688. try {
  689. this._instance.config.doc.designMode = 'on';
  690. } catch (e) { }
  691. this.focus();
  692. }
  693. return this;
  694. },
  695. /**
  696. * @method hide
  697. * @description Hide the iframe instance
  698. * @return {Frame}
  699. * @chainable
  700. */
  701. hide: function() {
  702. this._iframe.setStyles({
  703. position: 'absolute',
  704. left: '-999999px'
  705. });
  706. return this;
  707. }
  708. }, {
  709. /**
  710. * @static
  711. * @property THROTTLE_TIME
  712. * @description The throttle time for key events in IE
  713. * @type Number
  714. * @default 100
  715. */
  716. THROTTLE_TIME: 100,
  717. /**
  718. * @static
  719. * @property DOM_EVENTS
  720. * @description The DomEvents that the frame automatically attaches and bubbles
  721. * @type Object
  722. */
  723. DOM_EVENTS: {
  724. dblclick: 1,
  725. click: 1,
  726. paste: 1,
  727. mouseup: 1,
  728. mousedown: 1,
  729. keyup: 1,
  730. keydown: 1,
  731. keypress: 1,
  732. activate: 1,
  733. deactivate: 1,
  734. beforedeactivate: 1,
  735. focusin: 1,
  736. focusout: 1
  737. },
  738. /**
  739. * @static
  740. * @property DEFAULT_CSS
  741. * @description The default css used when creating the document.
  742. * @type String
  743. */
  744. //DEFAULT_CSS: 'html { height: 95%; } body { padding: 7px; background-color: #fff; font: 13px/1.22 arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small; } a, a:visited, a:hover { color: blue !important; text-decoration: underline !important; cursor: text !important; } img { cursor: pointer !important; border: none; }',
  745. DEFAULT_CSS: 'body { background-color: #fff; font: 13px/1.22 arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small; } a, a:visited, a:hover { color: blue !important; text-decoration: underline !important; cursor: text !important; } img { cursor: pointer !important; border: none; }',
  746. /**
  747. * @static
  748. * @property HTML
  749. * @description The template string used to create the iframe
  750. * @type String
  751. */
  752. //HTML: '<iframe border="0" frameBorder="0" marginWidth="0" marginHeight="0" leftMargin="0" topMargin="0" allowTransparency="true" width="100%" height="99%"></iframe>',
  753. HTML: '<iframe border="0" frameBorder="0" marginWidth="0" marginHeight="0" leftMargin="0" topMargin="0" allowTransparency="true" width="100%" height="99%"></iframe>',
  754. /**
  755. * @static
  756. * @property PAGE_HTML
  757. * @description The template used to create the page when created dynamically.
  758. * @type String
  759. */
  760. PAGE_HTML: '<html dir="{DIR}" lang="{LANG}"><head><title>{TITLE}</title>{META}<base href="{BASE_HREF}"/>{LINKED_CSS}<style id="editor_css">{DEFAULT_CSS}</style>{EXTRA_CSS}</head><body>{CONTENT}</body></html>',
  761. /**
  762. * @static
  763. * @method getDocType
  764. * @description Parses document.doctype and generates a DocType to match the parent page, if supported.
  765. * For IE8, it grabs document.all[0].nodeValue and uses that. For IE < 8, it falls back to Frame.DOC_TYPE.
  766. * @returns {String} The normalized DocType to apply to the iframe
  767. */
  768. getDocType: function() {
  769. var dt = Y.config.doc.doctype,
  770. str = Frame.DOC_TYPE;
  771. if (dt) {
  772. str = '<!DOCTYPE ' + dt.name + ((dt.publicId) ? ' ' + dt.publicId : '') + ((dt.systemId) ? ' ' + dt.systemId : '') + '>';
  773. } else {
  774. if (Y.config.doc.all) {
  775. dt = Y.config.doc.all[0];
  776. if (dt.nodeType) {
  777. if (dt.nodeType === 8) {
  778. if (dt.nodeValue) {
  779. if (dt.nodeValue.toLowerCase().indexOf('doctype') !== -1) {
  780. str = '<!' + dt.nodeValue + '>';
  781. }
  782. }
  783. }
  784. }
  785. }
  786. }
  787. return str;
  788. },
  789. /**
  790. * @static
  791. * @property DOC_TYPE
  792. * @description The DOCTYPE to prepend to the new document when created. Should match the one on the page being served.
  793. * @type String
  794. */
  795. DOC_TYPE: '<!DOCTYPE HTML PUBLIC "-/'+'/W3C/'+'/DTD HTML 4.01/'+'/EN" "http:/'+'/www.w3.org/TR/html4/strict.dtd">',
  796. /**
  797. * @static
  798. * @property META
  799. * @description The meta-tag for Content-Type to add to the dynamic document
  800. * @type String
  801. */
  802. META: '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/><meta http-equiv="X-UA-Compatible" content="IE=7">',
  803. //META: '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>',
  804. /**
  805. * @static
  806. * @property NAME
  807. * @description The name of the class (frame)
  808. * @type String
  809. */
  810. NAME: 'frame',
  811. ATTRS: {
  812. /**
  813. * @attribute title
  814. * @description The title to give the blank page.
  815. * @type String
  816. */
  817. title: {
  818. value: 'Blank Page'
  819. },
  820. /**
  821. * @attribute dir
  822. * @description The default text direction for this new frame. Default: ltr
  823. * @type String
  824. */
  825. dir: {
  826. value: 'ltr'
  827. },
  828. /**
  829. * @attribute lang
  830. * @description The default language. Default: en-US
  831. * @type String
  832. */
  833. lang: {
  834. value: 'en-US'
  835. },
  836. /**
  837. * @attribute src
  838. * @description The src of the iframe/window. Defaults to javascript:;
  839. * @type String
  840. */
  841. src: {
  842. //Hackish, IE needs the false in the Javascript URL
  843. value: 'javascript' + ((Y.UA.ie) ? ':false' : ':') + ';'
  844. },
  845. /**
  846. * @attribute designMode
  847. * @description Should designMode be turned on after creation.
  848. * @writeonce
  849. * @type Boolean
  850. */
  851. designMode: {
  852. writeOnce: true,
  853. value: false
  854. },
  855. /**
  856. * @attribute content
  857. * @description The string to inject into the body of the new frame/window.
  858. * @type String
  859. */
  860. content: {
  861. value: '<br>',
  862. setter: '_setHTML',
  863. getter: '_getHTML'
  864. },
  865. /**
  866. * @attribute basehref
  867. * @description The base href to use in the iframe.
  868. * @type String
  869. */
  870. basehref: {
  871. value: false,
  872. getter: '_resolveBaseHref'
  873. },
  874. /**
  875. * @attribute use
  876. * @description Array of modules to include in the scoped YUI instance at render time. Default: ['none', 'selector-css2']
  877. * @writeonce
  878. * @type Array
  879. */
  880. use: {
  881. writeOnce: true,
  882. value: ['substitute', 'node', 'node-style', 'selector-css3']
  883. },
  884. /**
  885. * @attribute container
  886. * @description The container to append the iFrame to on render.
  887. * @type String/HTMLElement/Node
  888. */
  889. container: {
  890. value: 'body',
  891. setter: function(n) {
  892. return Y.one(n);
  893. }
  894. },
  895. /**
  896. * @attribute node
  897. * @description The Node instance of the iframe.
  898. * @type Node
  899. */
  900. node: {
  901. readOnly: true,
  902. value: null,
  903. getter: function() {
  904. return this._iframe;
  905. }
  906. },
  907. /**
  908. * @attribute id
  909. * @description Set the id of the new Node. (optional)
  910. * @type String
  911. * @writeonce
  912. */
  913. id: {
  914. writeOnce: true,
  915. getter: function(id) {
  916. if (!id) {
  917. id = 'iframe-' + Y.guid();
  918. }
  919. return id;
  920. }
  921. },
  922. /**
  923. * @attribute linkedcss
  924. * @description An array of url's to external linked style sheets
  925. * @type String
  926. */
  927. linkedcss: {
  928. value: '',
  929. getter: '_getLinkedCSS',
  930. setter: '_setLinkedCSS'
  931. },
  932. /**
  933. * @attribute extracss
  934. * @description A string of CSS to add to the Head of the Editor
  935. * @type String
  936. */
  937. extracss: {
  938. value: '',
  939. setter: '_setExtraCSS'
  940. },
  941. /**
  942. * @attribute host
  943. * @description A reference to the Editor instance
  944. * @type Object
  945. */
  946. host: {
  947. value: false
  948. },
  949. /**
  950. * @attribute defaultblock
  951. * @description The default tag to use for block level items, defaults to: p
  952. * @type String
  953. */
  954. defaultblock: {
  955. value: 'p'
  956. }
  957. }
  958. });
  959. Y.Frame = Frame;
  960. }, '3.4.0' ,{skinnable:false, requires:['base', 'node', 'selector-css3', 'substitute', 'yui-throttle']});