Bug 20489 Configure illegal file characters https://bugzilla.wikimedia.org/show_bug...
[mediawiki.git] / js2 / mwEmbed / libClipEdit / pixastic-lib / pixastic-editor / editor.js
blob6789531dd083d2a56891e57d469b37114be95c63
2 var PixasticEditor = (function () {
4         var $frame;     // iframe container element
5         var $editor;    // editor container element
7         // various UI structures
8         var accordionElements = {};
9         var tabElements = {};
10         var activeTabId;
11         var $activeTabContent;
13         var isRunning = false;
15         var $loadingScreen;
17         var $imageCanvas;       // the canvas holding the current state of the image
18         var $displayCanvas;     // the canvas element displayed on the screen, also the working canvas (where preview operations are performed)
19         var imageCtx;
21         var imageWidth = 0;     // dimensions of the current image state
22         var imageHeight = 0;
24         var undoImages = [];    // canvas elements holding previous image states
25         var undoLevels = 10;
27         var doc;
29         var $;
31         // test for valid file formats for toDataURL()
32         // we do that by calling it with each of the mime types in testFormats
33         // and then doing string checking on the resulting data: URI to see if it succeeded
34         var saveFormats = [];
35         var testFormats = [["image/jpeg", "JPEG"], ["image/png", "PNG"]];
36         var testCanvas = document.createElement("canvas");
37         if (testCanvas.toDataURL) {
38                 testCanvas.width = testCanvas.height = 1;
39                 for (var i=0;i<testFormats.length;i++) {
40                         var data = testCanvas.toDataURL(testFormats[i][0]);
41                         if (data.substr(0, 5 + testFormats[i][0].length) == "data:" + testFormats[i][0])
42                                 saveFormats.push({mime:testFormats[i][0], name:testFormats[i][1]});
43                 }
44         }
47         // pops up an error dialog with the specified text (errTxt),
48         // if no context is provided, the name of the calling function is used.
49         // The final message is returned for easy throwing of actual errors
50         function errorDialog(errTxt, context) {
51                 if (!($editor && $editor.get && $editor.get(0)))
52                         throw new Error("errorDialog(): $editor doesn't exist");
54                 var caller = errorDialog.caller.toString().split(" ")[1];
55                 caller = caller.substring(0, caller.indexOf("("));
56                 context = context || caller;
57                 errTxt = context + "(): " + errTxt;
58                 var dialog = $j("<div></div>", doc)
59                         .addClass("error-dialog")
60                         .attr("title", "Oops!")
61                         .html(errTxt)
62                         .dialog();
63                 // the dialog is added outside the Pixastic container, so get it back in.
64                 var dialogParent = $j(dialog.get(0).parentNode);
65                 dialogParent.appendTo($editor);
67                 return errTxt;
68         }
69         
70         function enableTab(id, refresh) {
71                 if (id == activeTabId && !refresh)
72                         return;
74                 activeTabId = id;
76                 var activeIndex = 0;
78                 if ($activeTabContent) {
79                         if ($activeTabContent.get(0)) {
80                                 var $parent = $j($activeTabContent.get(0).parentNode);
81                                 activeIndex = $parent.data("accordionindex");
82                                 if ($parent.data("ondeactivate")) {
83                                         $parent.data("ondeactivate")();
84                                 }
85                                 if ($parent.data("previewCheckbox"))
86                                         $parent.data("previewCheckbox").attr("checked", false);
87                                 $parent.data("uidesc").previewEnabled = false;
88                                 if ($parent.data("uidesc").forcePreview)
89                                         $parent.data("uidesc").previewEnabled = true;
90                         }
91                 }
94                 for (var a in accordionElements) {
95                         if (accordionElements.hasOwnProperty(a)) {
96                                 accordionElements[a].accordion("option", "animated", false);
97                                 accordionElements[a].accordion("activate", -1);
98                                 accordionElements[a].hide();
99                                 tabElements[a].removeClass("active");
101                         }
102                 }
104                 accordionElements[id].accordion("option", "animated", false);
105                 accordionElements[id].accordion("activate", refresh ? activeIndex : 0);
106                 tabElements[id].addClass("active");
107                 accordionElements[id].show();
108                 accordionElements[id].accordion("option", "animated", "slide");
109                 resetDisplayCanvas();
110         }
112         // revert to a previous image state
113         function undo(idx) {
114                 var undoImage = undoImages[idx];
116                 if (!undoImage) 
117                         throw new Error(errorDialog("Invalid undo state"));
118                 if (!($imageCanvas && $imageCanvas.get && $imageCanvas.get(0)))
119                         throw new Error(errorDialog("$imageCanvas doesn't exist"));
121                 var canvas = $imageCanvas.get(0);
122                 addUndo(canvas);
123                 canvas.width = imageWidth = undoImage.width;
124                 canvas.height = imageHeight = undoImage.height;
125                 canvas.getContext("2d").drawImage(undoImage,0,0);
127                 enableTab(activeTabId, true);
128                 resetDisplayCanvas();
129         }
131         function addUndo(canvasElement) {
132                 if (!canvasElement)
133                         throw new Error(errorDialog("No undo image state provided"));
135                 if (undoImages.length == undoLevels) {
136                         undoImages.shift();
137                 }
138                 var undoCanvas = document.createElement("canvas");
139                 undoCanvas.width = canvasElement.width;
140                 undoCanvas.height = canvasElement.height;
141                 undoCanvas.getContext("2d").drawImage(canvasElement,0,0);
142                 $j(undoCanvas).addClass("undo-canvas");
143                 undoImages.push(undoCanvas);
144                 updateUndoList();
145         }
147         function updateUndoList() {
148                 var $listCtr = $j("#undo-bar", doc)
149                         .html("");
151                 var ctrHeight = $listCtr.height();
153                 var $testCanvas = $j("<canvas></canvas>", doc)
154                         .addClass("undo-canvas-small")
155                         .addClass("far-far-away")
156                         .appendTo("body");
158                 var canvasHeight = $testCanvas.height();
159                 var canvasWidth = $testCanvas.width();
160                 var canvasCSSHeight = canvasHeight + parseInt($testCanvas.css("margin-top"),10) + parseInt($testCanvas.css("margin-bottom"),10);
162                 $testCanvas.remove();
164                 var undoRatio = canvasWidth / canvasHeight;
166                 for (var i=undoImages.length-1;i>=0;i--) {
167                         (function(){
168                                 var canvas = document.createElement("canvas");
169                                 $j(canvas)
170                                         .addClass("undo-canvas-small")
171                                         .attr("width", canvasWidth)
172                                         .attr("height", canvasHeight);
174                                 var image = undoImages[i];
175                                 $j(image).show();
176                                 
177                                 var undoWidth, undoHeight;
178                                 var imageRatio = image.width / image.height;
180                                 if (imageRatio > undoRatio) {   // image too wide
181                                         undoWidth = canvasWidth;
182                                         undoHeight = canvasWidth / imageRatio;
183                                 } else {
184                                         undoWidth = canvasHeight * imageRatio;
185                                         undoHeight = canvasHeight;
186                                 }
188                                 var restWidth = canvasWidth - undoWidth;
189                                 var restHeight = canvasHeight - undoHeight;
191                                 canvas.getContext("2d").drawImage(
192                                         image,
193                                         0,0,image.width,image.height,
194                                         restWidth*0.5, restHeight*0.5,
195                                         undoWidth, undoHeight
196                                 );
199                                 $link = $j("<a href='#'></a>", doc)
200                                         .addClass("undo-link")
201                                         .appendTo($listCtr)
202                                         .mouseover(function(){ $j(this).addClass("hover") })
203                                         .mouseout(function(){ $j(this).removeClass("hover") });
204                                 $j(canvas).appendTo($link);
206                                 var displayShowing;
207                                 var undoIndex = i;
208                                 $link.click(function() {
209                                         $j(image).hide();
210                                         $j(image).remove();
211                                         undo(undoIndex);
212                                         if (displayShowing)
213                                                 $displayCanvas.show();
214                                         $j(".jcrop-holder", doc).show();
215                                 });
217                                 $link.mouseover(function() {
218                                         displayShowing = $displayCanvas.css("display") != "none";
219                                         var $imagectr = $j("#image-container", doc);
221                                         $j(".jcrop-holder", doc).hide();
222                                         $displayCanvas.hide();
223                                         $j(image).appendTo($imagectr);
225                                         var h1 = $j("#image-area", doc).height();
226                                         var h2 = image.height;
227                                         var m = Math.max(0, (h1 - h2) / 2);
228                                         $imagectr.css("marginTop", m);
229                         
230                                         $imagectr.height(image.height);
231                                 });
233                                 $link.mouseout(function() {
234                                         $j(image).remove();
235                                         if (displayShowing)
236                                                 $displayCanvas.show();
237                                         $j(".jcrop-holder", doc).show();
238                                         updateDisplayCanvas();
239                                 });
242                                 $j(canvas).attr("title", "Click to revert to this previous image");
244                         })();
245                 }
246         }
249         function applyAction(id, options, afteraction) {
250                 if (!Pixastic.Actions[id])
251                         throw new Error("applyAction(): unknown action [" + id + "]");
253                 $j("#action-bar-overlay", doc).show();
255                 setTimeout(function() {
256                         options.leaveDOM = true;
257                         var canvasElement = $imageCanvas.get(0);
258                         addUndo(canvasElement)
259         
260                         var res = Pixastic.process(
261                                 canvasElement, id, options,
262                                 function(resCanvas) {
263                                         canvasElement.width = imageWidth = resCanvas.width;
264                                         canvasElement.height = imageHeight = resCanvas.height;
265         
266                                         var ctx = canvasElement.getContext("2d");
267                                         ctx.clearRect(0,0,imageWidth,imageHeight);
268                                         ctx.drawImage(resCanvas,0,0);
269                                         $imageCanvas = $j(canvasElement);
270                                         resetDisplayCanvas();
271         
272                                         $j("#action-bar-overlay", doc).hide();
274                                         if (afteraction)
275                                                 afteraction();
276                                 }
277                         );
278                         if (!res)
279                                 throw new Error("applyAction(): Pixastic.process() failed for action [" + id + "]");
280                 },1);
281         }
284         function previewAction(id, options, afteraction) {
285                 if (!Pixastic.Actions[id])
286                         throw new Error("applyAction(): unknown action [" + id + "]");
288                 $j("#action-bar-overlay", doc).show();
290                 resetDisplayCanvas();
292                 options.leaveDOM = true;
293                 var canvasElement = $displayCanvas.get(0);
295                 var res = Pixastic.process(
296                         canvasElement, id, options,
297                         function(resCanvas) {
299                                 canvasElement.width = resCanvas.width;
300                                 canvasElement.height = resCanvas.height;
302                                 var ctx = canvasElement.getContext("2d");
303                                 ctx.clearRect(0,0,canvasElement.width,canvasElement.height);
304                                 ctx.drawImage(resCanvas,0,0);
305                                 updateDisplayCanvas();
306                                 updateOverlay();
308                                 $j("#action-bar-overlay", doc).hide();
310                                 if (afteraction)
311                                         afteraction();
312                         }
313                 );
314         }
316         var onwindowresize = function() {
317                 updateDisplayCanvas();
318                 updateOverlay();
319         }
321         var baseUrl = ""
323         function buildEditor() {
324                 var styles = [
325                         "jquery-ui-1.7.1.custom.css",
326                         "jquery.Jcrop.css",
327                         "pixastic.css"
328                 ];
330                 for (var i=0;i<styles.length;i++) {
331                         var s = doc.createElement("link");
332                         s.href = baseUrl + styles[i];
333                         s.type = "text/css";
334                         s.rel = "stylesheet";
335                         doc.getElementsByTagName("head")[0].appendChild( s );
336                 }
338                 undoImages = [];
339                 accordionElements = {};
340                 tabElements = {};
341                 activeTabId = -1;
342                 $activeTabContent = null;
344                 // setup DOM UI skeleton
345                 $editor = $j("<div />", doc)
346                         .attr("id", "pixastic-editor")
347                         .appendTo($j(doc.body));
349                 $editor.append(
350                         $j("<div id='background' />", doc),
351                         $j("<div id='edit-ctr-1' />", doc).append(
352                                 $j("<div id='edit-ctr-2' />", doc).append(
353                                         $j("<div id='controls-bar' />", doc).append(
354                                                 $j("<div id='action-bar' />", doc).append(
355                                                         $j("<div id='action-bar-overlay' />", doc)
356                                                 ),
357                                                 $j("<div id='undo-bar' />", doc)
358                                         ),
359                                         $j("<div id='image-area' />", doc).append(
360                                                 $j("<div id='image-area-sub' />", doc).append(
361                                                         $j("<div id='image-container' />", doc),
362                                                         $j("<div id='image-overlay-container' />", doc).append(
363                                                                 $j("<div id='image-overlay' />", doc)
364                                                         )
365                                                 )
366                                         )
367                                 )
368                         ),
369                         $j("<div id='main-bar' />", doc),
370                         $j("<div id='powered-by-pixastic'><a href=\"http://www.pixastic.com/\" target=\"_blank\">Powered by Pixastic</a></div>", doc)
371                 );
373                 $j("#image-container", doc).append(
374                         $displayCanvas = $j("<canvas />", doc)
375                                 .addClass("display-canvas")
376                 );
378                 // loop through all  defined UI action controls
379                 var tabs = PixasticEditor.UI.data.tabs;
381                 for (var i=0;i<tabs.length;i++) {
382                         (function() {
383         
384                         var tab = tabs[i];
386                         var $tabElement = $j("<a href=\"#\">" + tab.title + "</a>", doc)
387                                 .attr("id", "main-tab-button-" + tab.id)
388                                 .addClass("main-tab")
389                                 .click(function() {
390                                         enableTab(tab.id);
391                                 })
392                                 .mouseover(function(){ $j(this).addClass("hover") })
393                                 .mouseout(function(){ $j(this).removeClass("hover") });
394         
395                         $j("#main-bar", doc).append($tabElement);
397                         tabElements[tab.id] = $tabElement;
399                         var $menu = $j("<div/>", doc);
400                         accordionElements[tab.id] = $menu;
402                         for (var j=0;j<tab.actions.length;j++) {
403                                 (function() {
405                                 var action = tab.actions[j];
407                                 var $actionElement = $j("<div><h3><a href=\"#\">" + action.title + "</a></h3></div>", doc)
409                                 $menu.append($actionElement);
411                                 var $content = $j("<div></div>", doc)
412                                         .attr("id", "pixastic-action-tab-content-" + action.id)
413                                         .appendTo($actionElement);
415                                 var controlOptions = [];
417                                 action.previewEnabled = false;
418                                 if (action.forcePreview)
419                                         action.previewEnabled = true;
421                                 function togglePreview(enable, doAction) {
422                                         if (enable && !action.previewEnabled && doAction)
423                                                 doAction(true);
424                                         if (!enable && action.previewEnabled)
425                                                 resetDisplayCanvas();
426                         
427                                         action.previewEnabled = enable;
428                                 }
430                                 var reset = function() {
431                                         for (var i in controlOptions) {
432                                                 if (controlOptions.hasOwnProperty(i)) {
433                                                         controlOptions[i].reset();
434                                                 }
435                                         }
436                                         if (action.previewEnabled)
437                                                 doAction(true);
438                                 }
439                                 var doAction = function(isPreview) {
440                                         var options = {};
441                                         for (var i in controlOptions) {
442                                                 if (controlOptions.hasOwnProperty(i)) {
443                                                         options[i] = controlOptions[i].valueField.val();
444                                                 }
445                                         }
447                                         var afteraction = function() {
448                                                 if (action.onafteraction)
449                                                         action.onafteraction(action, isPreview);
450                                                 if (!isPreview)
451                                                         resetDisplayCanvas();
452         
453                                                 if (!isPreview && !action.forcePreview) {
454                                                         $j("#pixastic-input-preview-" + action.id, doc).attr("checked", false);
455                                                         togglePreview(false);
456                                                         reset();
457                                                 }
458                                         }
460                                         if (isPreview) {
461                                                 previewAction(action.id, options, afteraction);
462                                         } else {
463                                                 applyAction(action.id, options, afteraction);
464                                         }
466                                 }
468                                 var hadInputs = false;
470                                 if (action.controls) {
471                                         var onChange = function() {};
472                                         if (action.isAction && action.preview) {
473                                                 onChange = function() {
474                                                         if (action.previewEnabled)
475                                                                 doAction(true)
476                                                 };
477                                         }
479                                         for (var k=0;k<action.controls.length;k++) {
480                                                 var control = action.controls[k];
481                                                 if (typeof control.defaultValue != "function") {
482                                                         (function(){
483                                                         var defVal = control.defaultValue;
484                                                         control.defaultValue = function() {
485                                                                 return defVal;
486                                                         }
487                                                         })();
488                                                 }
489                                                 var controlId = action.id + "-" + control.option;
491                                                 if (control.type != "output")
492                                                         hadInputs = true;
494                                                 switch (control.type) {
495                                                         case "number" :
496                                                                 switch (control.ui) {
497                                                                         case "slider" : 
498                                                                                 var slider = PixasticEditor.UI.makeSlider(
499                                                                                         control.label, controlId, 
500                                                                                         control.range[0], control.range[1], control.step, control.defaultValue, onChange
501                                                                                 );
502                 
503                                                                                 slider.container.appendTo($content);
504                                                                                 controlOptions[control.option] = slider;
505                                                                                 break;
506                                                                         case "text" : 
507                                                                                 var text = PixasticEditor.UI.makeNumericInput(
508                                                                                         control.label, control.labelRight, controlId, 
509                                                                                         control.range[0], control.range[1], control.step, control.defaultValue, onChange
510                                                                                 );
511                                                                                 text.container.appendTo($content);
512                                                                                 controlOptions[control.option] = text;
513                                                                                 break;
514                                                                 }
515                                                                 break;
516                                                         case "boolean" :
517                                                                 switch (control.ui) {
518                                                                         case "checkbox" : 
519                                                                                 var checkbox = PixasticEditor.UI.makeCheckbox(
520                                                                                         control.label, controlId, control.defaultValue, onChange
521                                                                                 );
522                 
523                                                                                 checkbox.container.appendTo($content);
524                                                                                 controlOptions[control.option] = checkbox;
525                                                                                 break;
526                                                                 }
527                                                         case "string" :
528                                                                 switch (control.ui) {
529                                                                         case "select" : 
530                                                                                 var select = PixasticEditor.UI.makeSelect(
531                                                                                         control.label, controlId, control.values, control.defaultValue, onChange
532                                                                                 );
533                 
534                                                                                 select.container.appendTo($content);
535                                                                                 controlOptions[control.option] = select;
536                                                                                 break;
537                                                                 }
538                                                                 break;
539                                                         case "output" :
540                                                                 var outputText = $j("<div></div>", doc)
541                                                                         .addClass("ui-action-output")
542                                                                         .html(control.content)
543                                                                         .appendTo($content);
544                                                                 break;
545                                                 }
546                                         }
547                                 }
549                                 if (action.isAction) {
551                                         var $applyButton = PixasticEditor.UI.makeButton("Apply")
552                                                 .addClass("pixastic-option-button-apply")
553                                                 .click(function() {doAction();});
555                                         $content.append($applyButton);
557                                         if (hadInputs) {
558                                                 var $resetButton = PixasticEditor.UI.makeButton("Reset")
559                                                         .addClass("pixastic-option-button-reset")
560                                                         .click(reset);
561         
562                                                 $content.append($resetButton)
563                                         }
565                                         if (action.preview && !action.forcePreview) {
566                                                 var $checkctr = $j("<div></div>", doc)
567                                                         .addClass("ui-checkbox-container")
568                                                         .addClass("ui-preview-checkbox-container");
570                                                 var $label = $j("<label></label>", doc)
571                                                         .addClass("ui-checkbox-label")
572                                                         .attr("for", "pixastic-input-preview-" + action.id)
573                                                         .html("Preview:")
574                                                         .appendTo($checkctr);
576                                                 var $checkbox = $j("<input type=\"checkbox\"></input>", doc)
577                                                         .addClass("ui-checkbox")
578                                                         .attr("id", "pixastic-input-preview-" + action.id)
579                                                         .appendTo($checkctr)
580                                                         .change(function() {
581                                                                 togglePreview(this.checked, doAction)
582                                                         });
584                                                 $content.append($checkctr);
586                                                 $content.data("previewCheckbox", $checkbox);
587                                         }
589                                 }
592                                 if (typeof action.content == "function") {
593                                         action.content($content);
594                                 }
596                                 // stupid hack to make it possible to get $content in change event (below)
597                                 $j("<span></span>", doc).appendTo($content);
599                                 $content.data("controlOptions", controlOptions);
600                                 $content.data("onactivate", action.onactivate);
601                                 $content.data("ondeactivate", action.ondeactivate);
602                                 $content.data("onoverlayupdate", action.onoverlayupdate);
603                                 $content.data("accordionindex", j);
604                                 $content.data("uidesc", action);
606                                 })();
607                         }
608         
609                         $j("#action-bar", doc).append($menu);
611                         $menu.hide().accordion({
612                                 header: "h3",
613                                 autoHeight : false,
614                                 collapsible : true,
615                                 active: -1
616                         })
617                         .bind("accordionchange", 
618                                 function(event, ui) {
619                                         resetDisplayCanvas();
621                                         // oldContent / newContent are arrays of whatever elements are present in the content area
622                                         // We need the parent element (the one holding the content) but if there is no content, how do we get it?
623                                         // fixed above by always appending a <span> but that's ugly and needs to be done in some other way
624                                         if (ui.oldContent.get(0)) {
625                                                 var $parent = $j(ui.oldContent.get(0).parentNode);
626                                                 if ($parent.data("ondeactivate")) {
627                                                         $parent.data("ondeactivate")();
628                                                 }
629                                         }
630                                         $activeTabContent = ui.newContent;
632                                         if (ui.newContent.get(0)) {
633                                                 var $parent = $j(ui.newContent.get(0).parentNode);
634                                                 if ($parent.data("previewCheckbox"))
635                                                         $parent.data("previewCheckbox").attr("checked", false);
636                                                 $parent.data("uidesc").previewEnabled = false;
637                                                 if ($parent.data("uidesc").forcePreview)
638                                                         $parent.data("uidesc").previewEnabled = true;
640                                                 var controlOptions = $parent.data("controlOptions");
641                                                 for (var i in controlOptions) {
642                                                         if (controlOptions.hasOwnProperty(i)) {
643                                                                 controlOptions[i].reset();
644                                                         }
645                                                 }
646                                                 if ($parent.data("onactivate")) {
647                                                         $parent.data("onactivate")();
648                                                 }
649                                         }
650                                         updateDisplayCanvas();
652                                 }
653                         );
655         
656                         })();
657                 }
659                 $j(window).bind("resize", onwindowresize);
660         }
662         function showLoadingScreen() {
663                 if ($loadingScreen) {
664                         $loadingScreen.show();
665                         return;
666                 }
667                 $loadingScreen = $j("<div id=\"loading-screen\" />")
668                 var $ctr = $j("<div id=\"loading-screen-cell\" />");
669                 $j("<div />")
670                         .addClass("spinner")
671                         .appendTo($ctr);
672                 $loadingScreen.append($ctr);
673                 $loadingScreen.appendTo("body");
674         }
676         function hideLoadingScreen() {
677                 setTimeout(function() {
678                         $loadingScreen.hide();
679                 }, 1);
680         }
682         var oldScrollLeft;
683         var oldScrollTop;
684         var oldOverflow;
686         // fire it up
687         function init(callback) {
688                 isRunning = true;
690                 showLoadingScreen();
692                 oldScrollLeft = document.body.scrollLeft;
693                 oldScrollTop = document.body.scrollTop;
694                 oldOverflow = document.body.style.overflow;
696                 document.body.scrollLeft = 0;
697                 document.body.scrollTop = 0;
698                 document.body.style.overflow = "hidden";
700                 $frame = $j("<iframe />");
701                 $frame.hide();
702                 $frame.css({
703                         position : "absolute",
704                         left : document.body.scrollLeft + "px",
705                         top : document.body.scrollTop + "px",
706                         width : "100%",
707                         height : "100%",
708                         zIndex : "11"
709                 });
710                 $frame.load(function(){
711                         doc = $frame.get(0).contentDocument;
713                         buildEditor();
714                         callback();
715                         $frame.show();
716                         hideLoadingScreen();
717                         setTimeout(function(){
718                                 updateDisplayCanvas();
719                         },10);
720                 });
721                 $frame.appendTo("body");
722         }
724         // unload the editor, remove all elements added to the page and restore whatever properties we messed with
725         function unload() {
726                 $j(window).unbind("resize", onwindowresize);
727                 $frame.hide();
728                 $editor.hide();
729                 $editor.remove();
730                 $frame.remove();
732                 document.body.scrollLeft = oldScrollLeft;
733                 document.body.scrollTop = oldScrollTop;
734                 document.body.style.overflow = oldOverflow;
736                 isRunning = false;
737         }
740         // resets the display canvas (clears the canvas and repaints the current state)
741         // then updates display and overlay
742         function resetDisplayCanvas() {
743                 if (!($displayCanvas && $displayCanvas.get))    throw new Error(errorDialog("$displayCanvas doesn't exist"));
744                 if (!($imageCanvas && $imageCanvas.get))        throw new Error(errorDialog("$imageCanvas doesn't exist"));
746                 var display = $displayCanvas.get(0);
747                 var image = $imageCanvas.get(0);
749                 if (!display)   throw new Error(errorDialog("resetDisplayCanvas(): No elements in $displayCanvas"));
750                 if (!image)     throw new Error(errorDialog("resetDisplayCanvas(): No elements in $imageCanvas"));
752                 display.width = imageWidth;
753                 display.height = imageHeight;
754                 display.getContext("2d").drawImage( image, 0, 0 );
756                 updateDisplayCanvas();
757                 updateOverlay();
758         }
760         // updates the display by resetting the height and margin of the image container
761         // this is mainly to keep vertical centering
762         function updateDisplayCanvas() {
763                 var $imageCtr = $j("#image-container", doc);
764                 var $editArea = $j("#image-area", doc);
766                 if (!$imageCtr.get(0))          throw new Error(errorDialog("updateDisplayCanvas(): $imageCtr doesn't exist"));
767                 if (!$displayCanvas.get(0))     throw new Error(errorDialog("updateDisplayCanvas(): $displayCanvas doesn't exist"));
768                 if (!$editArea.get(0))          throw new Error(errorDialog("updateDisplayCanvas(): $editArea doesn't exist"));
770                 var h2 = $displayCanvas.get(0).height;
771                 var h1 = $j("#image-area", doc).height();
772                 var m = Math.max(0, (h1 - h2) / 2);
773                 $imageCtr.height(h2);
774                 $imageCtr.css("marginTop", m);
775         }
777         // basically the same as updateDisplayCanvas but for the image overlay
778         function updateOverlay() {
779                 var $overlay = $j("#image-overlay-container", doc);
780                 var $imagectr = $j("#image-container", doc);
781                 $overlay.height($imagectr.height());
782                 $overlay.css("marginTop", $imagectr.css("marginTop"));
784                 if ($activeTabContent && $activeTabContent.get(0)) {
785                         var $tabContent = $j($activeTabContent.get(0).parentNode);
786                         if (typeof $tabContent.data("onoverlayupdate") == "function")
787                                 $tabContent.data("onoverlayupdate")();
788                 }
789         }
791         var imageIsLoading = false;
792         var originalImageElement;
793         var $tmpImg;
795         function loadImage(imgEl) {
796                 if (imageIsLoading) 
797                         return;
799                 imageIsLoading = true;
801                 originalImageElement = imgEl;
803                 $imageCanvas = $j("<canvas />", doc);
804                 imageCtx = $imageCanvas.get(0).getContext("2d");
806                 imageWidth = 0;
807                 imageHeight = 0;
808                 $imageCanvas.attr("width", 0);
809                 $imageCanvas.attr("height", 0);
811                 if (imgEl.tagName.toLowerCase() == "img" && !imgEl._pixasticCanvas) {
812                         var onload = function(el) {
813                                 imageWidth = el.offsetWidth;
814                                 imageHeight = el.offsetHeight;
815                                 $imageCanvas.attr("width", imageWidth);
816                                 $imageCanvas.attr("height", imageHeight);
817                                 imageCtx.drawImage(el,0,0);
818                                 $tmpImg.remove();
819                                 imageIsLoading = false;
820                                 enableTab("reshape");
821                                 setTimeout(function() {
822                                         resetDisplayCanvas();
823                                 }, 10);
824                         }
825                         $tmpImg = $j("<img />", doc)
826                                 .css("position", "absolute")
827                                 .css("left", "-9999px")
828                                 .css("top", "-9999px")
829                                 .appendTo("body")
830                                 .load(function(){onload(this);})
831                                 .error(function(){
832                                         throw new Error("Could not load temporary copy image. Is provided image valid?");
833                                         unload();
834                                 })
835                                 .attr("src", imgEl.src);
836                                 if ($tmpImg.attr("complete")) {
837                                         onload($tmpImg.get(0));
838                                 }
839                 } else {
840                         var $canvas = imgEl._pixasticCanvas || imgEl;
841                         imageWidth = $canvas.attr("width");
842                         imageHeight = $canvas.attr("height");
843                         $imageCanvas.attr("width", imageWidth);
844                         $imageCanvas.attr("height", imageHeight);
845                         imageCtx.drawImage($canvas.get(0), 0, 0);
846                         imageIsLoading = false;
847                         enableTab("reshape");
848                         resetDisplayCanvas();
849                 }
850         }
852         // return public interface
853         return {
854                 /*
855                 // don't call. For now we must load the image immediately via load()
856                 loadImage : function(imgEl) {
857                         if (!isRunning) return false;
858                         loadImage(imgEl);
859                 },
860                 */
861                 saveToPage : function() {
862                         if (!isRunning) throw new Error("PixasticEditor::saveToPage(): Editor is not running");
864                         var $canvas = PixasticEditor.getImageCanvas();
865                         var img = PixasticEditor.getOriginalImage();
866                         if (img.tagName.toLowerCase() == "canvas") {
867                                 img.width = $canvas.attr("width");
868                                 img.height = $canvas.attr("height");
869                                 img.getContext("2d").drawImage($canvas.get(0), 0, 0);
870                         } else {
871                                 img.src = PixasticEditor.getDataURI();
872                         }
873                         img._pixasticCanvas = PixasticEditor.getImageCanvas();
874                 },
875                 load : function(img, customBaseUrl) {
876                         if (isRunning) return false;
878                         if (!img)
879                                 throw new Error("Must be called with an image or canvas as its first argument", "PixasticEditor::load")
881                         $ = PixasticEditor.jQuery;
883                         baseUrl = customBaseUrl || "http://www.pixastic.com/editor-test/";
885                         init(function() {
886                                 if (img && img.tagName.toLowerCase() == "img" || img.tagName.toLowerCase() == "canvas") {
887                                         loadImage(img);
888                                 }
889                         });
890                 },
892                 unload : function() {
893                         if (!isRunning) throw new Error("PixasticEditor::unload(): Editor is not running");
894                         unload();
895                 },
897                 getDocument : function() {
898                         if (!isRunning) throw new Error("PixasticEditor::getDocument(): Editor is not running");
900                         return doc;
901                 },
903                 validSaveFormats : function() {
904                         return saveFormats;
905                 },
907                 getOriginalImage : function() {
908                         if (!isRunning) throw new Error("PixasticEditor::getOriginalImage(): Editor is not running");
909                         return originalImageElement;
910                 },
912                 getDataURI : function(mime) {
913                         if (!isRunning) throw new Error("PixasticEditor::getDataURI(): Editor is not running");
915                         if (!($imageCanvas && $imageCanvas.get && $imageCanvas.get(0)))
916                                 throw new Error(errorDialog("$imageCanvas doesn't exist", "getImageCanvas"));
918                         return $imageCanvas.get(0).toDataURL(mime||"image/png");
919                 },
921                 getImageCanvas : function() {
922                         if (!isRunning) throw new Error("PixasticEditor::getImageCanvas(): Editor is not running");
924                         if (!($imageCanvas && $imageCanvas.get && $imageCanvas.get(0)))
925                                 throw new Error(errorDialog("$imageCanvas doesn't exist", "getImageCanvas"));
927                         return $imageCanvas;
928                 },
929                 getOverlay : function() {
930                         if (!isRunning) throw new Error("PixasticEditor::getOverlay(): Editor is not running");
932                         return $j("#image-overlay", doc);
933                 },
934                 getDisplayCanvas : function() {
935                         if (!isRunning) throw new Error("PixasticEditor::getDisplayCanvas(): Editor is not running");
937                         if (!($displayCanvas && $displayCanvas.get && $displayCanvas.get(0)))
938                                 throw new Error(errorDialog("$displayCanvas doesn't exist", "getDisplayCanvas"));
939                         return $displayCanvas;
940                 },
941                 getDisplayWidth : function() {
942                         if (!isRunning) throw new Error("PixasticEditor::getDisplayWidth(): Editor is not running");
944                         return displayWidth;
945                 },
946                 getDisplayHeight : function() {
947                         if (!isRunning) throw new Error("PixasticEditor::getDisplayHeight(): Editor is not running");
949                         return displayHeight;
950                 },
951                 getImageWidth : function() {
952                         if (!isRunning) throw new Error("PixasticEditor::getImageWidth(): Editor is not running");
954                         return imageWidth;
955                 },
956                 getImageHeight : function() {
957                         if (!isRunning) throw new Error("PixasticEditor::getImageHeight(): Editor is not running");
959                         return imageHeight;
960                 },
961                 errorDialog : function() {
962                         if (!isRunning) throw new Error("PixasticEditor::errorDialog(): Editor is not running");
964                         return errorDialog.apply(null, arguments);
965                 }
966         }
968 })();