LocalImage.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. define("dojox/editor/plugins/LocalImage", [
  2. "dojo",//FIXME
  3. "dijit",//FIXME
  4. "dijit/registry",
  5. "dijit/_base/popup",
  6. "dijit/_editor/_Plugin",
  7. "dijit/_editor/plugins/LinkDialog",
  8. "dijit/TooltipDialog",
  9. "dijit/form/_TextBoxMixin",
  10. "dijit/form/Button",
  11. "dijit/form/ValidationTextBox",
  12. "dijit/form/DropDownButton",
  13. "dojo/_base/connect",
  14. "dojo/_base/declare",
  15. "dojo/_base/sniff",
  16. "dojox/form/FileUploader", //FIXME: deprecated. Use Uploader instead
  17. "dojo/i18n!dojox/editor/plugins/nls/LocalImage"
  18. ], function(dojo, dijit, registry, popup, _Plugin, LinkDialog, TooltipDialog,
  19. _TextBoxMixin, Button, ValidationTextBox, DropDownButton,
  20. connect, declare, has, FileUploader, messages) {
  21. var LocalImage = dojo.declare("dojox.editor.plugins.LocalImage", LinkDialog.ImgLinkDialog, {
  22. // summary:
  23. // This plugin provides an enhanced image link dialog that
  24. // not only insert the online images, but upload the local image files onto
  25. // to server then insert them as well.
  26. // Dependencies:
  27. // This plugin depends on dojox.form.FileUploader to upload the images on the local driver.
  28. // Do the regression test whenever FileUploader is upgraded.
  29. // uploadable [public] Boolean
  30. // Indicate whether the user can upload a local image file onto the server.
  31. // If it is set to true, the Browse button will be available.
  32. uploadable: false,
  33. // uploadUrl [public] String
  34. // The url targeted for uploading. Both absolute and relative URLs are OK.
  35. uploadUrl: "",
  36. // baseImageUrl [public] String
  37. // The prefix of the image url on the server.
  38. // For example, an image is uploaded and stored at the following location
  39. // http://www.myhost.com/images/uploads/test.jpg.
  40. // When the image is uploaded, the server returns "uploads/test.jpg" as the
  41. // relative path. So the baseImageUrl should be set to "http://www.myhost.com/images/"
  42. // so that the client can retrieve the image from the server.
  43. // If the image file is located on the same domain as that of the current web page,
  44. // baseImageUrl can be a relative path. For example:
  45. // baseImageUrl = images/
  46. // and the server returns uploads/test.jpg
  47. // The complete URL of the image file is images/upload/test.jpg
  48. baseImageUrl: "",
  49. // fileMask [public] String
  50. // Specify the types of images that are allowed to be uploaded.
  51. // Note that the type checking on server is also very important!
  52. fileMask: "*.jpg;*.jpeg;*.gif;*.png;*.bmp",
  53. // urlRegExp [protected] String
  54. // Used to validate if the input is a valid image URL.
  55. urlRegExp: "",
  56. // htmlFieldName [private] htmlFieldName
  57. htmlFieldName:"uploadedfile",
  58. // _isLocalFile [private] Boolean
  59. // Indicate if a local file is to be uploaded to the server
  60. // If false, the text of _urlInput field is regarded as the
  61. // URL of the online image
  62. _isLocalFile: false,
  63. // _messages [private] Array<String>
  64. // Contains i18n strings.
  65. _messages: "",
  66. // _cssPrefix [private] String
  67. // The prefix of the CSS style
  68. _cssPrefix: "dijitEditorEilDialog",
  69. // _closable [private] Boolean
  70. // Indicate if the tooltip dialog can be closed. Used to workaround Safari 5 bug
  71. // where the file dialog doesn't pop up in modal until after the first click.
  72. _closable: true,
  73. // linkDialogTemplate [protected] String
  74. // Over-ride for template since this is an enhanced image dialog.
  75. linkDialogTemplate: [
  76. "<div style='border-bottom: 1px solid black; padding-bottom: 2pt; margin-bottom: 4pt;'></div>", // <hr/> breaks the dialog in IE6
  77. "<div class='dijitEditorEilDialogDescription'>${prePopuTextUrl}${prePopuTextBrowse}</div>",
  78. "<table><tr><td colspan='2'>",
  79. "<label for='${id}_urlInput' title='${prePopuTextUrl}${prePopuTextBrowse}'>${url}</label>",
  80. "</td></tr><tr><td class='dijitEditorEilDialogField'>",
  81. "<input dojoType='dijit.form.ValidationTextBox' class='dijitEditorEilDialogField'" +
  82. "regExp='${urlRegExp}' title='${prePopuTextUrl}${prePopuTextBrowse}' selectOnClick='true' required='true' " +
  83. "id='${id}_urlInput' name='urlInput' intermediateChanges='true' invalidMessage='${invalidMessage}' " +
  84. "prePopuText='&lt;${prePopuTextUrl}${prePopuTextBrowse}&gt'>",
  85. "</td><td>",
  86. "<div id='${id}_browse' style='display:${uploadable}'>${browse}</div>",
  87. "</td></tr><tr><td colspan='2'>",
  88. "<label for='${id}_textInput'>${text}</label>",
  89. "</td></tr><tr><td>",
  90. "<input dojoType='dijit.form.TextBox' required='false' id='${id}_textInput' " +
  91. "name='textInput' intermediateChanges='true' selectOnClick='true' class='dijitEditorEilDialogField'>",
  92. "</td><td></td></tr><tr><td>",
  93. "</td><td>",
  94. "</td></tr><tr><td colspan='2'>",
  95. "<button dojoType='dijit.form.Button' id='${id}_setButton'>${set}</button>",
  96. "</td></tr></table>"
  97. ].join(""),
  98. _initButton: function(){
  99. // summary:
  100. // Override _Plugin._initButton() to initialize DropDownButton and TooltipDialog.
  101. // tags:
  102. // protected
  103. var _this = this;
  104. this._messages = messages;
  105. this.tag = "img";
  106. var dropDown = (this.dropDown = new TooltipDialog({
  107. title: messages[this.command + "Title"],
  108. onOpen: function(){
  109. _this._initialFileUploader();
  110. _this._onOpenDialog();
  111. TooltipDialog.prototype.onOpen.apply(this, arguments);
  112. setTimeout(function(){
  113. // Auto-select the text if it is not empty
  114. _TextBoxMixin.selectInputText(_this._urlInput.textbox);
  115. _this._urlInput.isLoadComplete = true;
  116. }, 0);
  117. },
  118. onClose: function(){
  119. dojo.disconnect(_this.blurHandler);
  120. _this.blurHandler = null;
  121. this.onHide();
  122. },
  123. onCancel: function(){
  124. setTimeout(dojo.hitch(_this, "_onCloseDialog"),0);
  125. }
  126. }));
  127. var label = this.getLabel(this.command),
  128. className = this.iconClassPrefix + " " + this.iconClassPrefix + this.command.charAt(0).toUpperCase() + this.command.substr(1),
  129. props = dojo.mixin({
  130. label: label,
  131. showLabel: false,
  132. iconClass: className,
  133. dropDown: this.dropDown,
  134. tabIndex: "-1"
  135. }, this.params || {});
  136. if(!has("ie")){
  137. // Workaround for Non-IE problem:
  138. // Safari 5: After the select-file dialog opens, the first time the user clicks anywhere (even on that dialog)
  139. // it's treated like a plain click on the page, and the tooltip dialog closes
  140. // FF & Chrome: the select-file dialog does not block the execution of JS
  141. props.closeDropDown = function(/*Boolean*/ focus){
  142. if(_this._closable){
  143. if(this._opened){
  144. popup.close(this.dropDown);
  145. if(focus){ this.focus(); }
  146. this._opened = false;
  147. this.state = "";
  148. }
  149. }
  150. setTimeout(function(){ _this._closable = true; }, 10);
  151. };
  152. }
  153. this.button = new DropDownButton(props);
  154. // Generate the RegExp of the ValidationTextBox from fileMask
  155. // *.jpg;*.png => /.*\.jpg|.*\.JPG|.*\.png|.*\.PNG/
  156. var masks = this.fileMask.split(";"),
  157. temp = "";
  158. dojo.forEach(masks, function(m){
  159. m = m.replace(/\./, "\\.").replace(/\*/g, ".*");
  160. temp += "|" + m + "|" + m.toUpperCase();
  161. });
  162. messages.urlRegExp = this.urlRegExp = temp.substring(1);
  163. if(!this.uploadable){
  164. messages.prePopuTextBrowse = ".";
  165. }
  166. messages.id = registry.getUniqueId(this.editor.id);
  167. messages.uploadable = this.uploadable ? "inline" : "none";
  168. this._uniqueId = messages.id;
  169. this._setContent("<div class='" + this._cssPrefix + "Title'>" + dropDown.title + "</div>" +
  170. dojo.string.substitute(this.linkDialogTemplate, messages));
  171. dropDown.startup();
  172. var urlInput = (this._urlInput = registry.byId(this._uniqueId + "_urlInput"));
  173. this._textInput = registry.byId(this._uniqueId + "_textInput");
  174. this._setButton = registry.byId(this._uniqueId + "_setButton");
  175. if(urlInput){
  176. var pt = ValidationTextBox.prototype;
  177. urlInput = dojo.mixin(urlInput, {
  178. // Indicate if the widget is ready to validate the input text
  179. isLoadComplete: false,
  180. isValid: function(isFocused){
  181. if(this.isLoadComplete){
  182. return pt.isValid.apply(this, arguments);
  183. }else{
  184. return this.get("value").length > 0;
  185. }
  186. },
  187. reset: function(){
  188. this.isLoadComplete = false;
  189. pt.reset.apply(this, arguments);
  190. }
  191. });
  192. this.connect(urlInput, "onKeyDown", "_cancelFileUpload");
  193. this.connect(urlInput, "onChange", "_checkAndFixInput");
  194. }
  195. if(this._setButton){
  196. this.connect(this._setButton, "onClick", "_checkAndSetValue");
  197. }
  198. this._connectTagEvents();
  199. },
  200. _initialFileUploader: function(){
  201. // summary:
  202. // Initialize the FileUploader and connect up its events
  203. // tags:
  204. // private
  205. var fup = null,
  206. _this = this,
  207. widgetId = _this._uniqueId,
  208. fUpId = widgetId + "_browse",
  209. urlInput = _this._urlInput;
  210. if(_this.uploadable && !_this._fileUploader){
  211. fup = _this._fileUploader = new FileUploader({
  212. force: "html", // Noticed that SWF may cause browsers to crash sometimes
  213. uploadUrl: _this.uploadUrl,
  214. htmlFieldName: _this.htmlFieldName,
  215. uploadOnChange: false,
  216. selectMultipleFiles: false,
  217. showProgress: true
  218. }, fUpId);
  219. // TooltipDialog will call reset on all the widgets contained within it.
  220. // Have FileUploader be responsive to this call.
  221. fup.reset = function(){
  222. _this._isLocalFile = false;
  223. fup._resetHTML();
  224. };
  225. _this.connect(fup, "onClick", function(){
  226. urlInput.validate(false);
  227. if(!has("ie")){
  228. // Firefox, Chrome and Safari have a strange behavior:
  229. // When the File Upload dialog is open, the browse div (FileUploader) will lose its focus
  230. // and triggers onBlur event. This event will cause the whole tooltip dialog
  231. // to be closed when the File Upload dialog is open. The popup dialog should hang up
  232. // the js execution rather than triggering an event. IE does not have such a problem.
  233. _this._closable = false;
  234. }
  235. });
  236. _this.connect(fup, "onChange", function(data){
  237. _this._isLocalFile = true;
  238. urlInput.set("value", data[0].name); //Single selection
  239. urlInput.focus();
  240. });
  241. _this.connect(fup, "onComplete", function(data){
  242. var urlPrefix = _this.baseImageUrl;
  243. urlPrefix = urlPrefix && urlPrefix.charAt(urlPrefix.length - 1) == "/" ? urlPrefix : urlPrefix + "/";
  244. urlInput.set("value", urlPrefix + data[0].file); //Single selection
  245. _this._isLocalFile = false;
  246. _this._setDialogStatus(true);
  247. _this.setValue(_this.dropDown.get("value"));
  248. });
  249. _this.connect(fup, "onError", function(evtObject){
  250. // summary:
  251. // Fires on errors
  252. console.log("Error occurred when uploading image file!");
  253. _this._setDialogStatus(true);
  254. });
  255. }
  256. },
  257. _checkAndFixInput: function(){
  258. // summray:
  259. // Over-ride the original method
  260. this._setButton.set("disabled", !this._isValid());
  261. },
  262. _isValid: function(){
  263. // summray:
  264. // Invalid cases: URL is not ended with the suffix listed
  265. return this._urlInput.isValid();
  266. },
  267. _cancelFileUpload: function(){
  268. this._fileUploader.reset();
  269. this._isLocalFile = false;
  270. },
  271. _checkAndSetValue: function(){
  272. // summray:
  273. // Determine if a local file is to be uploaded.
  274. // If a local file is to be uploaded, do not close the dialog
  275. // until the file uploading is finished. Else, insert the image directly into the editor.
  276. // tags:
  277. // private
  278. if(this._fileUploader && this._isLocalFile){
  279. this._setDialogStatus(false);
  280. this._fileUploader.upload();
  281. }else{
  282. this.setValue(this.dropDown.get("value"));
  283. }
  284. },
  285. _setDialogStatus: function(/*Boolean*/ value){
  286. this._urlInput.set("disabled", !value);
  287. this._textInput.set("disabled", !value);
  288. this._setButton.set("disabled", !value);
  289. },
  290. destroy: function(){
  291. // summary:
  292. // Cleanup of the plugin.
  293. this.inherited(arguments);
  294. if(this._fileUploader){
  295. this._fileUploader.destroy();
  296. delete this._fileUploader;
  297. }
  298. }
  299. });
  300. // Register this plugin.
  301. _Plugin.registry["LocalImage"] = function(args){
  302. return new LocalImage({
  303. command: "insertImage",
  304. uploadable: ("uploadable" in args) ? args.uploadable : false,
  305. uploadUrl: ("uploadable" in args && "uploadUrl" in args) ? args.uploadUrl : "",
  306. htmlFieldName: ("uploadable" in args && "htmlFieldName" in args) ? args.htmlFieldName : "uploadedfile",
  307. baseImageUrl: ("uploadable" in args && "baseImageUrl" in args) ? args.baseImageUrl : "",
  308. fileMask: ("fileMask" in args) ? args.fileMask : "*.jpg;*.jpeg;*.gif;*.png;*.bmp"
  309. });
  310. };
  311. return LocalImage;
  312. });