1 /*
  2  * © 2009 ROBO Design
  3  * http://www.robodesign.ro
  4  *
  5  * $Date: 2009-04-21 14:30:39 +0300 $
  6  */
  7 
  8 /**
  9  * @author <a lang="ro" href="http://www.robodesign.ro/mihai">Mihai Şucan</a>
 10  * @fileOverview The paint application core code.
 11  */
 12 
 13 /**
 14  * @class The paint tool application object.
 15  */
 16 function Painter () {
 17   var _self = this;
 18 
 19   /**
 20    * Holds the buffer canvas and context references.
 21    * @type Object
 22    */
 23   this.buffer = {canvas: null, context: null};
 24 
 25   /**
 26    * Holds the current layer ID, canvas and context references.
 27    * @type Object
 28    */
 29   this.layer = {id: null, canvas: null, context: null};
 30 
 31   /**
 32    * The instance of the active tool object.
 33    *
 34    * @type Object
 35    * @see PainterConfig.tool_default holds the ID of the tool which is activated 
 36    * when the application loads.
 37    */
 38   this.tool = null;
 39 
 40   /**
 41    * Holds references to important DOM elements.
 42    *
 43    * @private
 44    * @type Object
 45    */
 46   this.elems = {};
 47 
 48   /**
 49    * Holds the keyboard event listener object.
 50    *
 51    * @private
 52    * @type lib.dom.KeyboardEventListener
 53    * @see lib.dom.KeyboardEventListener The class dealing with the cross-browser 
 54    * differences in the DOM keyboard events.
 55    */
 56   var kbListener_;
 57 
 58   /**
 59    * Initialize the paint application.
 60    * @private
 61    */
 62   function init () {
 63     if (!window.lang) {
 64       alert('Error: The language object is not available!');
 65       return;
 66     }
 67 
 68     if (!window.PaintTools) {
 69       alert(lang.PaintToolsNotFound);
 70       return;
 71     }
 72 
 73     if (!window.PainterConfig) {
 74       alert(lang.PainterConfigNotFound);
 75       return;
 76     }
 77 
 78     // This application does not yet implement layers support.
 79     // However, there's only little additional work to be done for layers 
 80     // support.
 81     _self.layer.id = 1;
 82 
 83     // Find the canvas element.
 84     _self.layer.canvas = document.getElementById('imageLayer');
 85     if (!_self.layer.canvas) {
 86       alert(lang.errorCanvasNotFound);
 87       return;
 88     }
 89 
 90     if (!_self.layer.canvas.getContext) {
 91       alert(lang.errorGetContext);
 92       return;
 93     }
 94 
 95     // Get the 2D canvas context.
 96     _self.layer.context = _self.layer.canvas.getContext('2d');
 97     if (!_self.layer.context) {
 98       alert(lang.errorGetContext);
 99       return;
100     }
101 
102     // Add the buffer canvas.
103     var container = _self.layer.canvas.parentNode;
104     _self.buffer.canvas = document.createElement('canvas');
105     if (!_self.buffer.canvas) {
106       alert(lang.errorCanvasCreate);
107       return;
108     }
109 
110     _self.buffer.canvas.id     = 'imageBuffer';
111     _self.buffer.canvas.width  = _self.layer.canvas.width;
112     _self.buffer.canvas.height = _self.layer.canvas.height;
113     container.appendChild(_self.buffer.canvas);
114 
115     _self.buffer.context = _self.buffer.canvas.getContext('2d');
116 
117     // Get the tools drop-down.
118     _self.elems.tool_select = document.getElementById('tool');
119     if (!_self.elems.tool_select) {
120       alert(lang.errorToolSelect);
121       return;
122     }
123     _self.elems.tool_select.addEventListener('change', ev_tool_change, false);
124 
125     // Activate the default tool.
126     _self.toolActivate(PainterConfig.tool_default);
127 
128     // Attach the mousedown, mousemove and mouseup event listeners.
129     _self.buffer.canvas.addEventListener('mousedown', ev_canvas, false);
130     _self.buffer.canvas.addEventListener('mousemove', ev_canvas, false);
131     _self.buffer.canvas.addEventListener('mouseup',   ev_canvas, false);
132 
133     // Add the keydown event listener.
134     kbListener_ = new lib.dom.KeyboardEventListener(window,
135         {keydown: ev_keydown});
136   };
137 
138   /**
139    * The Canvas event handler.
140    * 
141    * <p>This method determines the mouse position relative to the canvas 
142    * element, after which it invokes the method of the currently active tool 
143    * with the same name as the current event type. For example, for the 
144    * <code>mousedown</code> event the <code><var>tool</var>.mousedown()</code> 
145    * method is invoked.
146    *
147    * <p>The mouse coordinates are added to the <var>ev</var> DOM Event object: 
148    * <var>ev.x_</var> and <var>ev.y_</var>.
149    *
150    * @private
151    * @param {Event} ev The DOM Event object.
152    */
153   function ev_canvas (ev) {
154     if (typeof ev.layerX != 'undefined') { // Firefox
155       ev.x_ = ev.layerX;
156       ev.y_ = ev.layerY;
157     } else if (typeof ev.offsetX != 'undefined') { // Opera
158       ev.x_ = ev.offsetX;
159       ev.y_ = ev.offsetY;
160     }
161 
162     // Call the event handler of the active tool.
163     var func = _self.tool[ev.type];
164     if (typeof func == 'function') {
165       func(ev);
166     }
167   };
168 
169   /**
170    * The event handler for any changes made to the tool selector.
171    *
172    * @private
173    * @see Painter#toolActivate The method which does the actual drawing tool 
174    * activation.
175    */
176   function ev_tool_change () {
177     _self.toolActivate(this.value);
178   };
179 
180   /**
181    * Activate a drawing tool by ID.
182    *
183    * <p>The <var>id</var> provided must be available in the global {@link 
184    * PaintTools} object.
185    *
186    * @param {String} id The ID of the drawing tool to be activated.
187    *
188    * @returns {Boolean} True if the tool has been activated, or false if not.
189    *
190    * @see PaintTools The object holding all the drawing tools.
191    */
192   this.toolActivate = function (id) {
193     if (!id) {
194       return false;
195     }
196 
197     // Find the tool object.
198     var tool = PaintTools[id];
199     if (!tool) {
200       return false;
201     }
202 
203     // Check if the current tool is the same as the desired one.
204     if (_self.tool && _self.tool instanceof tool) {
205       return true;
206     }
207 
208     // Construct the new tool object.
209     var tool_obj = new tool(_self);
210     if (!tool_obj) {
211       alert(lang.errorToolActivate);
212       return false;
213     }
214 
215     _self.tool = tool_obj;
216 
217     // Update the tool drop-down.
218     _self.elems.tool_select.value = id;
219 
220     return true;
221   };
222 
223   /**
224    * The global <code>keydown</code> event handler. This makes all the keyboard 
225    * shortcuts work in the web application.
226    *
227    * <p>This method determines the key the user pressed, based on the 
228    * <var>ev</var> DOM Event object, taking into consideration any browser 
229    * differences.
230    *
231    * <p>In {@link PainterConfig.keys} one can setup the keyboard shortcuts. If 
232    * the keyboard combination is found in that list, then the associated tool is 
233    * activated.
234    *
235    * @private
236    *
237    * @param {Event} ev The DOM Event object.
238    *
239    * @see PainterConfig.keys The keyboard shortcuts configuration.
240    * @see lib.dom.KeyboardEventListener The class dealing with the cross-browser 
241    * differences in the DOM keyboard events.
242    */
243   function ev_keydown (ev) {
244     if (!ev || !ev.key_) {
245       return;
246     }
247 
248     // Do not continue if the event target is some form input.
249     if (ev.target && ev.target.nodeName) {
250       switch (ev.target.nodeName.toLowerCase()) {
251         case 'input':
252         case 'select':
253           return;
254       }
255     }
256 
257     // Determine the key ID.
258     var i, kid = '',
259         kmods = {altKey: 'Alt', ctrlKey: 'Control', shiftKey: 'Shift'};
260     for (i in kmods) {
261       if (ev[i] && ev.key_ != kmods[i]) {
262         kid += kmods[i] + ' ';
263       }
264     }
265     kid += ev.key_;
266 
267     // If there's no event handler within the active tool, or if the event 
268     // handler does otherwise return false, then continue with the global 
269     // keyboard shortcuts.
270 
271     var gkey = PainterConfig.keys[kid];
272     if (!gkey) {
273       return;
274     }
275 
276     // Activate the tool associated with the current keyboard shortcut.
277     if (gkey.tool) {
278       _self.toolActivate(gkey.tool);
279     }
280 
281     if (ev.preventDefault) {
282       ev.preventDefault();
283     }
284   };
285 
286   /**
287    * This method draws the buffer canvas on top of the current image layer, 
288    * after which the buffer is cleared. This function is called each time when 
289    * the user completes a drawing operation.
290    */
291   this.layerUpdate = function () {
292     _self.layer.context.drawImage(_self.buffer.canvas, 0, 0);
293     _self.buffer.context.clearRect(0, 0, _self.buffer.canvas.width, _self.buffer.canvas.height);
294   };
295 
296   init();
297 };
298 
299 if(window.addEventListener) {
300 window.addEventListener('load', function () {
301   if (window.Painter) {
302     // Create a Painter object instance.
303     window.PainterInstance = new Painter();
304   }
305 }, false); }
306 
307 // vim:set spell spl=en fo=wan1croql tw=80 ts=2 sw=2 sts=2 sta et ai cin fenc=utf-8 ff=unix:
308