]>
git.wh0rd.org - tt-rss.git/blob - lib/dijit/_editor/plugins/ViewSource.js.uncompressed.js
1 define("dijit/_editor/plugins/ViewSource", [
2 "dojo/_base/array", // array.forEach
3 "dojo/_base/declare", // declare
4 "dojo/dom-attr", // domAttr.set
5 "dojo/dom-construct", // domConstruct.create domConstruct.place
6 "dojo/dom-geometry", // domGeometry.setMarginBox domGeometry.position
7 "dojo/dom-style", // domStyle.set
8 "dojo/_base/event", // event.stop
9 "dojo/i18n", // i18n.getLocalization
10 "dojo/keys", // keys.F12
11 "dojo/_base/lang", // lang.hitch
13 "dojo/sniff", // has("ie") has("webkit")
14 "dojo/_base/window", // win.body win.global
15 "dojo/window", // winUtils.getBox
16 "../../focus", // focus.focus()
18 "../../form/ToggleButton",
19 "../..", // dijit._scopeName
20 "../../registry", // registry.getEnclosingWidget()
21 "dojo/aspect", // Aspect commands for adice
22 "dojo/i18n!../nls/commands"
23 ], function(array
, declare
, domAttr
, domConstruct
, domGeometry
, domStyle
, event
, i18n
, keys
, lang
, on
, has
, win
,
24 winUtils
, focus
, _Plugin
, ToggleButton
, dijit
, registry
, aspect
){
27 // dijit/_editor/plugins/ViewSource
30 var ViewSource
= declare("dijit._editor.plugins.ViewSource",_Plugin
, {
32 // This plugin provides a simple view source capability. When view
33 // source mode is enabled, it disables all other buttons/plugins on the RTE.
34 // It also binds to the hotkey: CTRL-SHIFT-F11 for toggling ViewSource mode.
36 // stripScripts: [public] Boolean
37 // Boolean flag used to indicate if script tags should be stripped from the document.
41 // stripComments: [public] Boolean
42 // Boolean flag used to indicate if comment tags should be stripped from the document.
46 // stripComments: [public] Boolean
47 // Boolean flag used to indicate if iframe tags should be stripped from the document.
51 // readOnly: [const] Boolean
52 // Boolean flag used to indicate if the source view should be readonly or not.
53 // Cannot be changed after initialization of the plugin.
57 // _fsPlugin: [private] Object
58 // Reference to a registered fullscreen plugin so that viewSource knows
64 // Function to allow programmatic toggling of the view.
66 // For Webkit, we have to focus a very particular way.
67 // when swapping views, otherwise focus doesn't shift right
68 // but can't focus this way all the time, only for VS changes.
69 // If we did it all the time, buttons like bold, italic, etc
71 if(has("webkit")){this._vsFocused
= true;}
72 this.button
.set("checked", !this.button
.get("checked"));
76 _initButton: function(){
78 // Over-ride for creation of the resize button.
79 var strings
= i18n
.getLocalization("dijit._editor", "commands"),
81 this.button
= new ToggleButton({
82 label
: strings
["viewSource"],
83 ownerDocument
: editor
.ownerDocument
,
87 iconClass
: this.iconClassPrefix
+ " " + this.iconClassPrefix
+ "ViewSource",
89 onChange
: lang
.hitch(this, "_showSource")
92 // IE 7 has a horrible bug with zoom, so we have to create this node
93 // to cross-check later. Sigh.
95 this._ieFixNode
= domConstruct
.create("div", {
102 }, editor
.ownerDocumentBody
);
104 // Make sure readonly mode doesn't make the wrong cursor appear over the button.
105 this.button
.set("readOnly", false);
109 setEditor: function(/*dijit/Editor*/ editor){
111 // Tell the plugin which Editor it is associated with.
113 // The editor object to attach the print capability to.
114 this.editor = editor;
117 this.editor.addKeyHandler(keys.F12, true, true, lang.hitch(this, function(e){
118 // Move the focus before switching
119 // It'll focus back. Hiding a focused
120 // node causes issues.
125 // Call the focus shift outside of the handler.
126 setTimeout(lang.hitch(this, function(){
127 // We over-ride focus, so we just need to call.
133 _showSource: function(source){
135 // Function to toggle between the source and RTE views.
137 // Boolean value indicating if it should be in source mode or not.
140 var ed = this.editor;
141 var edPlugins = ed._plugins;
143 this._sourceShown = source;
146 if(!this.sourceArea){
147 this._createSourceView();
150 // Update the QueryCommandEnabled function to disable everything but
151 // the source view mode. Have to over-ride a function, then kick all
152 // plugins to check their state.
153 ed._sourceQueryCommandEnabled = ed.queryCommandEnabled;
154 ed.queryCommandEnabled = function(cmd){
155 return cmd.toLowerCase() === "viewsource";
157 this.editor.onDisplayChanged();
158 html = ed.get("value");
159 html = this._filter(html);
160 ed.set("value", html);
161 array.forEach(edPlugins, function(p){
162 // Turn off any plugins not controlled by queryCommandenabled.
163 if(p && !(p instanceof ViewSource) && p.isInstanceOf(_Plugin)){
164 p.set("disabled", true)
168 // We actually do need to trap this plugin and adjust how we
169 // display the textarea.
171 this._fsPlugin._getAltViewNode = function(){
172 return self.sourceArea;
176 this.sourceArea.value = html;
178 // Since neither iframe nor textarea have margin, border, or padding,
179 // just set sizes equal
180 this.sourceArea.style.height = ed.iframe.style.height;
181 this.sourceArea.style.width = ed.iframe.style.width;
182 domStyle.set(ed.iframe, "display", "none");
183 domStyle.set(this.sourceArea, {
187 var resizer = function(){
188 // function to handle resize events.
189 // Will check current VP and only resize if
191 var vp = winUtils.getBox(ed.ownerDocument);
193 if("_prevW" in this && "_prevH" in this){
194 // No actual size change, ignore.
195 if(vp.w === this._prevW && vp.h === this._prevH){
206 clearTimeout(this._resizer);
207 delete this._resizer;
209 // Timeout it to help avoid spamming resize on IE.
210 // Works for all browsers.
211 this._resizer = setTimeout(lang.hitch(this, function(){
212 delete this._resizer;
216 this._resizeHandle = on(window, "resize", lang.hitch(this, resizer));
218 //Call this on a delay once to deal with IE glitchiness on initial size.
219 setTimeout(lang.hitch(this, this._resize), 100);
221 //Trigger a check for command enablement/disablement.
222 this.editor.onNormalizedDisplayChanged();
224 this.editor.__oldGetValue = this.editor.getValue;
225 this.editor.getValue = lang.hitch(this, function(){
226 var txt = this.sourceArea.value;
227 txt = this._filter(txt);
231 this._setListener = aspect.after(this.editor, "setValue", lang.hitch(this, function(htmlTxt){
232 htmlTxt = htmlTxt || "";
233 htmlTxt = this._filter(htmlTxt);
234 this.sourceArea.value = htmlTxt;
237 // First check that we were in source view before doing anything.
238 // corner case for being called with a value of false and we hadn't
239 // actually been in source display mode.
240 if(!ed._sourceQueryCommandEnabled){
244 // Remove the set listener.
245 this._setListener.remove();
246 delete this._setListener;
248 this._resizeHandle.remove();
249 delete this._resizeHandle;
251 if(this.editor.__oldGetValue){
252 this.editor.getValue = this.editor.__oldGetValue;
253 delete this.editor.__oldGetValue;
256 // Restore all the plugin buttons state.
257 ed.queryCommandEnabled = ed._sourceQueryCommandEnabled;
259 html = this.sourceArea.value;
260 html = this._filter(html);
262 ed.set("value", html);
266 array.forEach(edPlugins, function(p){
267 // Turn back on any plugins we turned off.
268 if(p && p.isInstanceOf(_Plugin)){
269 p.set("disabled", false);
273 domStyle.set(this.sourceArea, "display", "none");
274 domStyle.set(ed.iframe, "display", "block");
275 delete ed._sourceQueryCommandEnabled;
277 //Trigger a check for command enablement/disablement.
278 this.editor.onDisplayChanged();
280 // Call a delayed resize to wait for some things to display in header/footer.
281 setTimeout(lang.hitch(this, function(){
282 // Make resize calls.
283 var parent = ed.domNode.parentNode;
285 var container = registry.getEnclosingWidget(parent);
286 if(container && container.resize){
297 updateState: function(){
299 // Over-ride for button state control for disabled to work.
300 this.button.set("disabled", this.get("disabled"));
305 // Internal function to resize the source view
308 var ed = this.editor;
309 var tbH = ed.getHeaderHeight();
310 var fH = ed.getFooterHeight();
311 var eb = domGeometry.position(ed.domNode);
313 // Styles are now applied to the internal source container, so we have
314 // to subtract them off.
315 var containerPadding = domGeometry.getPadBorderExtents(ed.iframe.parentNode);
316 var containerMargin = domGeometry.getMarginExtents(ed.iframe.parentNode);
318 var extents = domGeometry.getPadBorderExtents(ed.domNode);
321 h: eb.h - (tbH + extents.h + fH)
324 // Fullscreen gets odd, so we need to check for the FS plugin and
326 if(this._fsPlugin && this._fsPlugin.isFullscreen){
327 //Okay, probably in FS, adjust.
328 var vp = winUtils.getBox(ed.ownerDocument);
329 edb.w = (vp.w - extents.w);
330 edb.h = (vp.h - (tbH + extents.h + fH));
334 // IE is always off by 2px, so we have to adjust here
335 // Note that IE ZOOM is broken here. I can't get
340 // IE has a horrible zoom bug. So, we have to try and account for
341 // it and fix up the scaling.
343 var _ie7zoom = -this._ieFixNode.offsetTop / 1000;
344 edb.w = Math.floor((edb.w + 0.9) / _ie7zoom);
345 edb.h = Math.floor((edb.h + 0.9) / _ie7zoom);
348 domGeometry.setMarginBox(this.sourceArea, {
349 w: edb.w - (containerPadding.w + containerMargin.w),
350 h: edb.h - (containerPadding.h + containerMargin.h)
353 // Scale the parent container too in this case.
354 domGeometry.setMarginBox(ed.iframe.parentNode, {
359 _createSourceView: function(){
361 // Internal function for creating the source view area.
364 var ed = this.editor;
365 var edPlugins = ed._plugins;
366 this.sourceArea = domConstruct.create("textarea");
368 domAttr.set(this.sourceArea, "readOnly", true);
369 this._readOnly = true;
371 domStyle.set(this.sourceArea, {
377 domConstruct.place(this.sourceArea, ed.iframe, "before");
379 if(has("ie") && ed.iframe.parentNode.lastChild !== ed.iframe){
380 // There's some weirdo div in IE used for focus control
381 // But is messed up scaling the textarea if we don't config
382 // it some so it doesn't have a varying height.
383 domStyle.set(ed.iframe.parentNode.lastChild,{
393 // We also need to take over editor focus a bit here, so that focus calls to
394 // focus the editor will focus to the right node when VS is active.
395 ed._viewsource_oldFocus = ed.focus;
397 ed.focus = function(){
398 if(self._sourceShown){
399 self.setSourceAreaCaret();
403 delete this._vsFocused;
404 // Must focus edit node in this case (webkit only) or
405 // focus doesn't shift right, but in normal
406 // cases we focus with the regular function.
407 focus.focus(ed.editNode);
409 ed._viewsource_oldFocus();
418 for(i = 0; i < edPlugins.length; i++){
419 // We actually do need to trap this plugin and adjust how we
420 // display the textarea.
422 if(p && (p.declaredClass === "dijit._editor.plugins.FullScreen" ||
423 p.declaredClass === (dijit._scopeName +
424 "._editor.plugins.FullScreen"))){
430 // Found, we need to over-ride the alt-view node function
431 // on FullScreen with our own, chain up to parent call when appropriate.
432 this._fsPlugin._viewsource_getAltViewNode = this._fsPlugin._getAltViewNode;
433 this._fsPlugin._getAltViewNode = function(){
434 return self._sourceShown?self.sourceArea:this._viewsource_getAltViewNode();
438 // Listen to the source area for key events as well, as we need to be able to hotkey toggle
439 // it from there too.
440 this.connect(this.sourceArea, "onkeydown", lang.hitch(this, function(e){
441 if(this._sourceShown && e.keyCode == keys.F12 && e.ctrlKey && e.shiftKey){
443 this.button.set("checked", false);
444 setTimeout(lang.hitch(this, function(){ed.focus();}), 100);
450 _stripScripts: function(html){
452 // Strips out script tags from the HTML used in editor.
454 // The HTML to filter
458 // Look for closed and unclosed (malformed) script attacks.
459 html = html.replace(/<\s*script[^>]*>((.|\s)*?)<\\?\/\s*script\s*>/ig, "");
460 html = html.replace(/<\s*script\b([^<>]|\s)*>?/ig, "");
461 html = html.replace(/<[^>]*=(\s|)*[("|')]javascript:[^$1][(\s|.)]*[$1][^>]*>/ig, "");
466 _stripComments: function(html){
468 // Strips out comments from the HTML used in editor.
470 // The HTML to filter
474 html = html.replace(/<!--(.|\s){1,}?-->/g, "");
479 _stripIFrames: function(html){
481 // Strips out iframe tags from the content, to avoid iframe script
482 // style injection attacks.
484 // The HTML to filter
488 html = html.replace(/<\s*iframe[^>]*>((.|\s)*?)<\\?\/\s*iframe\s*>/ig, "");
493 _filter: function(html){
495 // Internal function to perform some filtering on the HTML.
497 // The HTML to filter
501 if(this.stripScripts){
502 html = this._stripScripts(html);
504 if(this.stripComments){
505 html = this._stripComments(html);
507 if(this.stripIFrames){
508 html = this._stripIFrames(html);
514 setSourceAreaCaret: function(){
516 // Internal function to set the caret in the sourceArea
518 var global = win.global;
519 var elem = this.sourceArea;
521 if(this._sourceShown && !this.readOnly){
523 if(this.sourceArea.createTextRange){
524 var range = elem.createTextRange();
525 range.collapse(true);
526 range.moveStart("character", -99999); // move to 0
527 range.moveStart("character", 0); // delta from 0 is the correct position
528 range.moveEnd("character", 0);
531 }else if(global.getSelection){
532 if(elem.setSelectionRange){
533 elem.setSelectionRange(0,0);
541 // Over-ride to remove the node used to correct for IE's
544 domConstruct.destroy(this._ieFixNode);
547 clearTimeout(this._resizer);
548 delete this._resizer;
550 if(this._resizeHandle){
551 this._resizeHandle.remove();
552 delete this._resizeHandle;
554 if(this._setListener){
555 this._setListener.remove();
556 delete this._setListener;
558 this.inherited(arguments);
562 // Register this plugin.
563 // For back-compat accept "viewsource" (all lowercase) too, remove in 2.0
564 _Plugin.registry["viewSource"] = _Plugin.registry["viewsource"] = function(args){
565 return new ViewSource({
566 readOnly: ("readOnly" in args)?args.readOnly:false,
567 stripComments: ("stripComments" in args)?args.stripComments:true,
568 stripScripts: ("stripScripts" in args)?args.stripScripts:true,
569 stripIFrames: ("stripIFrames" in args)?args.stripIFrames:true