PageRenderTime 37ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/files/jquery.wpaint/1.13.1/wPaint.js

https://gitlab.com/Mirros/jsdelivr
JavaScript | 978 lines | 712 code | 177 blank | 89 comment | 100 complexity | e610d579b606a6447fad657b89b0e439 MD5 | raw file
  1. /******************************************
  2. * Websanova.com
  3. *
  4. * Resources for web entrepreneurs
  5. *
  6. * @author Websanova
  7. * @copyright Copyright (c) 2012 Websanova.
  8. * @license This wPaint jQuery plug-in is dual licensed under the MIT and GPL licenses.
  9. * @link http://www.websanova.com
  10. * @github http://github.com/websanova/wPaint
  11. * @version Version 1.13.1
  12. *
  13. ******************************************/
  14. (function($)
  15. {
  16. $.fn.wPaint = function(option, settings)
  17. {
  18. if(typeof option === 'object')
  19. {
  20. settings = option;
  21. }
  22. else if(typeof option == 'string')
  23. {
  24. var values = [];
  25. var elements = this.each(function()
  26. {
  27. var data = $(this).data('_wPaint');
  28. if(data)
  29. {
  30. if(option == 'clear') { data.clearAll(); }
  31. else if(option == 'image' && settings === undefined) { values.push(data.getImage()); }
  32. else if(option == 'image' && settings !== undefined) { data.setImage(settings, true); }
  33. else if(option == 'imageBg' && settings !== undefined) { data.setBgImage(settings); }
  34. else if($.fn.wPaint.defaultSettings[option] !== undefined)
  35. {
  36. if(settings !== undefined) { data.settings[option] = settings; }
  37. else { values.push(data.settings[option]); }
  38. }
  39. }
  40. });
  41. if(values.length === 1) { return values[0]; }
  42. if(values.length > 0) { return values; }
  43. else { return elements; }
  44. }
  45. //clean up some variables
  46. settings = $.extend({}, $.fn.wPaint.defaultSettings, settings || {});
  47. settings.lineWidthMin = parseInt(settings.lineWidthMin);
  48. settings.lineWidthMax = parseInt(settings.lineWidthMax);
  49. settings.lineWidth = parseInt(settings.lineWidth);
  50. settings.fontSizeMin = parseInt(settings.fontSizeMin);
  51. settings.fontSizeMax = parseInt(settings.fontSizeMax);
  52. settings.fontSize = parseInt(settings.fontSize);
  53. return this.each(function()
  54. {
  55. var $elem = $(this);
  56. var _settings = jQuery.extend(true, {}, settings);
  57. //test for HTML5 canvas
  58. var test = document.createElement('canvas');
  59. if(!test.getContext)
  60. {
  61. $elem.html("Browser does not support HTML5 canvas, please upgrade to a more modern browser.");
  62. return false;
  63. }
  64. if($elem.data('_wPaint')) return false;
  65. var canvas = new Canvas(_settings, $elem);
  66. canvas.mainMenu = new MainMenu(canvas);
  67. canvas.textMenu = new TextMenu(canvas);
  68. if(_settings.imageBg) $elem.append(canvas.generateBg($elem.width(), $elem.height(), _settings.imageBg));
  69. $elem.append(canvas.generate($elem.width(), $elem.height()));
  70. $elem.append(canvas.generateTemp());
  71. $elem.append(canvas.generateTextInput());
  72. $elem
  73. .append(canvas.mainMenu.generate(canvas, canvas.textMenu))
  74. .append(canvas.textMenu.generate(canvas, canvas.mainMenu));
  75. //init the snap on the text menu
  76. canvas.mainMenu.moveTextMenu(canvas.mainMenu, canvas.textMenu);
  77. //init mode
  78. canvas.mainMenu.set_mode(canvas.mainMenu, canvas, _settings.mode);
  79. //pull from css so that it is dynamic
  80. var buttonSize = $("._wPaint_icon").outerHeight(true) - (parseInt($("._wPaint_icon").css('paddingTop').split('px')[0]) + parseInt($("._wPaint_icon").css('paddingBottom').split('px')[0]));
  81. canvas.mainMenu.menu.find("._wPaint_fillColorPicker").wColorPicker({
  82. mode: "click",
  83. initColor: _settings.fillStyle,
  84. buttonSize: buttonSize,
  85. showSpeed: 300,
  86. hideSpeed: 300,
  87. onSelect: function(color){
  88. canvas.settings.fillStyle = color;
  89. canvas.textInput.css({color: color});
  90. }
  91. });
  92. canvas.mainMenu.menu.find("._wPaint_strokeColorPicker").wColorPicker({
  93. mode: "click",
  94. initColor: _settings.strokeStyle,
  95. buttonSize: buttonSize,
  96. showSpeed: 300,
  97. hideSpeed: 300,
  98. onSelect: function(color){
  99. canvas.settings.strokeStyle = color;
  100. }
  101. });
  102. //must set width after append to get proper dimensions
  103. canvas.mainMenu.setWidth(canvas, canvas.mainMenu.menu);
  104. canvas.mainMenu.setWidth(canvas, canvas.textMenu.menu);
  105. if(_settings.image)
  106. {
  107. canvas.setImage(_settings.image, true);
  108. }
  109. else
  110. {
  111. canvas.addUndo();
  112. }
  113. $elem.data('_wPaint', canvas);
  114. });
  115. }
  116. var shapes = ['Rectangle', 'Ellipse', 'Line', 'Text'];
  117. $.fn.wPaint.defaultSettings = {
  118. mode : 'Pencil', // drawing mode - Rectangle, Ellipse, Line, Pencil, Eraser
  119. lineWidthMin : '0', // line width min for select drop down
  120. lineWidthMax : '10', // line widh max for select drop down
  121. lineWidth : '2', // starting line width
  122. fillStyle : '#FFFFFF', // starting fill style
  123. strokeStyle : '#FFFF00', // start stroke style
  124. fontSizeMin : '8', // min font size in px
  125. fontSizeMax : '20', // max font size in px
  126. fontSize : '12', // current font size for text input
  127. fontFamilyOptions : ['Arial', 'Courier', 'Times', 'Trebuchet', 'Verdana'], // available font families
  128. fontFamily : 'Arial', // active font family for text input
  129. fontTypeBold : false, // text input bold enable/disable
  130. fontTypeItalic : false, // text input italic enable/disable
  131. fontTypeUnderline : false, // text input italic enable/disable
  132. image : null, // preload image - base64 encoded data
  133. imageBg : null, // preload image bg, cannot be altered but saved with image
  134. drawDown : null, // function to call when start a draw
  135. drawMove : null, // function to call during a draw
  136. drawUp : null, // function to call at end of draw
  137. menu : ['undo', 'redo', 'clear','rectangle','ellipse','line','pencil','text','eraser','fillColor','lineWidth','strokeColor'], // menu items - appear in order they are set
  138. menuOrientation : 'horizontal', // orinetation of menu (horizontal, vertical)
  139. menuOffsetX : 5, // offset for menu (left)
  140. menuOffsetY : 5, // offset for menu (top)
  141. menuTitles : { // icon titles, replace any of the values to customize
  142. 'undo': 'undo',
  143. 'redo': 'redo',
  144. 'clear': 'clear',
  145. 'rectangle': 'rectangle',
  146. 'ellipse': 'ellipse',
  147. 'line': 'line',
  148. 'pencil': 'pencil',
  149. 'text': 'text',
  150. 'eraser': 'eraser',
  151. 'fillColor': 'fill color',
  152. 'lineWidth': 'line width',
  153. 'strokeColor': 'stroke color',
  154. 'bold': 'bold',
  155. 'italic': 'italic',
  156. 'underline': 'underline',
  157. 'fontSize': 'font size',
  158. 'fontFamily': 'font family'
  159. },
  160. disableMobileDefaults: false // disable default touchmove events for mobile (will prevent flipping between tabs and scrolling)
  161. };
  162. /**
  163. * Canvas class definition
  164. */
  165. function Canvas(settings, elem)
  166. {
  167. this.settings = settings;
  168. this.$elem = elem;
  169. this.mainMenu = null;
  170. this.textMenu = null;
  171. this.undoArray = [];
  172. this.undoCurrent = -1;
  173. this.undoMax = 10;
  174. this.draw = false;
  175. this.canvas = null;
  176. this.ctx = null;
  177. this.canvasTemp = null;
  178. this.ctxTemp = null;
  179. this.canvasBg = null;
  180. this.ctxBg = null;
  181. this.canvasTempLeftOriginal = null;
  182. this.canvasTempTopOriginal = null;
  183. this.canvasTempLeftNew = null;
  184. this.canvasTempTopNew = null;
  185. this.textInput = null;
  186. return this;
  187. }
  188. Canvas.prototype =
  189. {
  190. /*******************************************************************************
  191. * Generate canvases and events
  192. *******************************************************************************/
  193. generate: function(width, height)
  194. {
  195. this.canvas = document.createElement('canvas');
  196. this.ctx = this.canvas.getContext('2d');
  197. //create local reference
  198. var _self = this;
  199. $(this.canvas)
  200. .attr('width', width + 'px')
  201. .attr('height', height + 'px')
  202. .css({position: 'absolute', left: 0, top: 0})
  203. .mousedown(function(e)
  204. {
  205. e.preventDefault();
  206. e.stopPropagation();
  207. _self.draw = true;
  208. _self.callFunc(e, _self, 'Down');
  209. });
  210. $(document)
  211. .mousemove(function(e)
  212. {
  213. if(_self.draw) _self.callFunc(e, _self, 'Move');
  214. })
  215. .mouseup(function(e)
  216. {
  217. //make sure we are in draw mode otherwise this will fire on any mouse up.
  218. if(_self.draw)
  219. {
  220. _self.draw = false;
  221. _self.callFunc(e, _self, 'Up');
  222. }
  223. });
  224. this.bindMobile();
  225. return $(this.canvas);
  226. },
  227. bindMobile: function()
  228. {
  229. $(this.canvas).bind('touchstart touchmove touchend touchcancel', function ()
  230. {
  231. var touches = event.changedTouches, first = touches[0], type = "";
  232. switch (event.type)
  233. {
  234. case "touchstart": type = "mousedown"; break;
  235. case "touchmove": type = "mousemove"; break;
  236. case "touchend": type = "mouseup"; break;
  237. default: return;
  238. }
  239. var simulatedEvent = document.createEvent("MouseEvent");
  240. simulatedEvent.initMouseEvent(type, true, true, window, 1, first.screenX, first.screenY, first.clientX, first.clientY, false, false, false, false, 0/*left*/, null);
  241. first.target.dispatchEvent(simulatedEvent);
  242. event.preventDefault();
  243. });
  244. //eliminate browser defaults for
  245. if(this.settings.disableMobileDefaults) $(document).bind('touchmove', function(e) { e.preventDefault(); });
  246. },
  247. generateTemp: function()
  248. {
  249. this.canvasTemp = document.createElement('canvas');
  250. this.ctxTemp = this.canvasTemp.getContext('2d');
  251. $(this.canvasTemp).css({position: 'absolute'}).hide();
  252. return $(this.canvasTemp);
  253. },
  254. generateBg: function(width, height, data)
  255. {
  256. var _self = this;
  257. if(!this.canvasBg)
  258. {
  259. this.canvasBg = document.createElement('canvas');
  260. this.ctxBg = this.canvasBg.getContext('2d');
  261. $(this.canvasBg).attr('id', 'mofo').css({position: 'absolute', left: 0, top: 0}).attr('width', width).attr('height', height);
  262. }
  263. this.setBgImage(data);
  264. return $(this.canvasBg);
  265. },
  266. generateTextInput: function()
  267. {
  268. var _self = this;
  269. _self.textCalc = $('<div></div>').css({display:'none', fontSize:this.settings.fontSize, lineHeight:this.settings.fontSize+'px', fontFamily:this.settings.fontFamily});
  270. _self.textInput =
  271. $('<textarea class="_wPaint_textInput" spellcheck="false"></textarea>')
  272. .css({display:'none', position:'absolute', color:this.settings.fillStyle, fontSize:this.settings.fontSize, lineHeight:this.settings.fontSize+'px', fontFamily:this.settings.fontFamily})
  273. if(_self.settings.fontTypeBold) { _self.textInput.css('fontWeight', 'bold'); _self.textCalc.css('fontWeight', 'bold'); }
  274. if(_self.settings.fontTypeItalic) { _self.textInput.css('fontStyle', 'italic'); _self.textCalc.css('fontStyle', 'italic'); }
  275. if(_self.settings.fontTypeUnderline) { _self.textInput.css('textDecoration', 'underline'); _self.textCalc.css('textDecoration', 'underline'); }
  276. $('body').append(_self.textCalc);
  277. return _self.textInput;
  278. },
  279. callFunc: function(e, _self, event)
  280. {
  281. $e = jQuery.extend(true, {}, e);
  282. var canvas_offset = $(_self.canvas).offset();
  283. $e.pageX = Math.floor($e.pageX - canvas_offset.left);
  284. $e.pageY = Math.floor($e.pageY - canvas_offset.top);
  285. var mode = $.inArray(_self.settings.mode, shapes) > -1 ? 'Shape' : _self.settings.mode;
  286. var func = _self['draw' + mode + '' + event];
  287. if(func) func($e, _self);
  288. if(_self.settings['draw' + event]) _self.settings['draw' + event].apply(_self, [e, mode]);
  289. if(_self.settings.mode !== 'Text' && event === 'Up') { this.addUndo(); }
  290. },
  291. /*******************************************************************************
  292. * draw any shape
  293. *******************************************************************************/
  294. drawShapeDown: function(e, _self)
  295. {
  296. if(_self.settings.mode == 'Text')
  297. {
  298. //draw current text before resizing next text box
  299. if(_self.textInput.val() != '') _self.drawTextUp(e, _self);
  300. _self.textInput.css({left: e.pageX-1, top: e.pageY-1, width:0, height:0});
  301. }
  302. $(_self.canvasTemp)
  303. .css({left: e.pageX, top: e.pageY})
  304. .attr('width', 0)
  305. .attr('height', 0)
  306. .show();
  307. _self.canvasTempLeftOriginal = e.pageX;
  308. _self.canvasTempTopOriginal = e.pageY;
  309. var func = _self['draw' + _self.settings.mode + 'Down'];
  310. if(func) func(e, _self);
  311. },
  312. drawShapeMove: function(e, _self)
  313. {
  314. var xo = _self.canvasTempLeftOriginal;
  315. var yo = _self.canvasTempTopOriginal;
  316. var half_line_width = _self.settings.lineWidth / 2;
  317. var left = (e.pageX < xo ? e.pageX : xo) - (_self.settings.mode == 'Line' ? Math.floor(half_line_width) : 0);
  318. var top = (e.pageY < yo ? e.pageY : yo) - (_self.settings.mode == 'Line' ? Math.floor(half_line_width) : 0);
  319. var width = Math.abs(e.pageX - xo) + (_self.settings.mode == 'Line' ? _self.settings.lineWidth : 0);
  320. var height = Math.abs(e.pageY - yo) + (_self.settings.mode == 'Line' ? _self.settings.lineWidth : 0);
  321. $(_self.canvasTemp)
  322. .css({left: left, top: top})
  323. .attr('width', width)
  324. .attr('height', height)
  325. if(_self.settings.mode == 'Text') _self.textInput.css({left: left-1, top: top-1, width:width, height:height});
  326. _self.canvasTempLeftNew = left;
  327. _self.canvasTempTopNew = top;
  328. var func = _self['draw' + _self.settings.mode + 'Move'];
  329. if(func)
  330. {
  331. var factor = _self.settings.mode == 'Line' ? 1 : 2;
  332. e.x = half_line_width*factor;
  333. e.y = half_line_width*factor;
  334. e.w = width - _self.settings.lineWidth*factor;
  335. e.h = height - _self.settings.lineWidth*factor;
  336. _self.ctxTemp.fillStyle = _self.settings.fillStyle;
  337. _self.ctxTemp.strokeStyle = _self.settings.strokeStyle;
  338. _self.ctxTemp.lineWidth = _self.settings.lineWidth*factor;
  339. func(e, _self);
  340. }
  341. },
  342. drawShapeUp: function(e, _self)
  343. {
  344. if(_self.settings.mode != 'Text')
  345. {
  346. _self.ctx.drawImage(_self.canvasTemp ,_self.canvasTempLeftNew, _self.canvasTempTopNew);
  347. $(_self.canvasTemp).hide();
  348. var func = _self['draw' + _self.settings.mode + 'Up'];
  349. if(func) func(e, _self);
  350. }
  351. },
  352. /*******************************************************************************
  353. * draw rectangle
  354. *******************************************************************************/
  355. drawRectangleMove: function(e, _self)
  356. {
  357. _self.ctxTemp.beginPath();
  358. _self.ctxTemp.rect(e.x, e.y, e.w, e.h)
  359. _self.ctxTemp.closePath();
  360. _self.ctxTemp.stroke();
  361. _self.ctxTemp.fill();
  362. },
  363. /*******************************************************************************
  364. * draw ellipse
  365. *******************************************************************************/
  366. drawEllipseMove: function(e, _self)
  367. {
  368. var kappa = .5522848;
  369. var ox = (e.w / 2) * kappa; // control point offset horizontal
  370. var oy = (e.h / 2) * kappa; // control point offset vertical
  371. var xe = e.x + e.w; // x-end
  372. var ye = e.y + e.h; // y-end
  373. var xm = e.x + e.w / 2; // x-middle
  374. var ym = e.y + e.h / 2; // y-middle
  375. _self.ctxTemp.beginPath();
  376. _self.ctxTemp.moveTo(e.x, ym);
  377. _self.ctxTemp.bezierCurveTo(e.x, ym - oy, xm - ox, e.y, xm, e.y);
  378. _self.ctxTemp.bezierCurveTo(xm + ox, e.y, xe, ym - oy, xe, ym);
  379. _self.ctxTemp.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
  380. _self.ctxTemp.bezierCurveTo(xm - ox, ye, e.x, ym + oy, e.x, ym);
  381. _self.ctxTemp.closePath();
  382. if(_self.settings.lineWidth > 0)_self.ctxTemp.stroke();
  383. _self.ctxTemp.fill();
  384. },
  385. /*******************************************************************************
  386. * draw line
  387. *******************************************************************************/
  388. drawLineMove: function(e, _self)
  389. {
  390. var xo = _self.canvasTempLeftOriginal;
  391. var yo = _self.canvasTempTopOriginal;
  392. if(e.pageX < xo) { e.x = e.x + e.w; e.w = e.w * -1}
  393. if(e.pageY < yo) { e.y = e.y + e.h; e.h = e.h * -1}
  394. _self.ctxTemp.lineJoin = "round";
  395. _self.ctxTemp.beginPath();
  396. _self.ctxTemp.moveTo(e.x, e.y);
  397. _self.ctxTemp.lineTo(e.x + e.w, e.y + e.h);
  398. _self.ctxTemp.closePath();
  399. _self.ctxTemp.stroke();
  400. },
  401. /*******************************************************************************
  402. * draw pencil
  403. *******************************************************************************/
  404. drawPencilDown: function(e, _self)
  405. {
  406. _self.ctx.lineJoin = "round";
  407. _self.ctx.lineCap = "round";
  408. _self.ctx.strokeStyle = _self.settings.strokeStyle;
  409. _self.ctx.fillStyle = _self.settings.strokeStyle;
  410. _self.ctx.lineWidth = _self.settings.lineWidth;
  411. //draw single dot in case of a click without a move
  412. _self.ctx.beginPath();
  413. _self.ctx.arc(e.pageX, e.pageY, _self.settings.lineWidth/2, 0, Math.PI*2, true);
  414. _self.ctx.closePath();
  415. _self.ctx.fill();
  416. //start the path for a drag
  417. _self.ctx.beginPath();
  418. _self.ctx.moveTo(e.pageX, e.pageY);
  419. },
  420. drawPencilMove: function(e, _self)
  421. {
  422. _self.ctx.lineTo(e.pageX, e.pageY);
  423. _self.ctx.stroke();
  424. },
  425. drawPencilUp: function(e, _self)
  426. {
  427. _self.ctx.closePath();
  428. },
  429. /*******************************************************************************
  430. * draw text
  431. *******************************************************************************/
  432. drawTextDown: function(e, _self)
  433. {
  434. _self.textInput.val('').show().focus();
  435. },
  436. drawTextUp: function(e, _self)
  437. {
  438. if(e) { this.addUndo(); }
  439. var fontString = '';
  440. if(_self.settings.fontTypeItalic) fontString += 'italic ';
  441. //if(_self.settings.fontTypeUnderline) fontString += 'underline ';
  442. if(_self.settings.fontTypeBold) fontString += 'bold ';
  443. fontString += _self.settings.fontSize + 'px ' + _self.settings.fontFamily;
  444. //setup lines
  445. var lines = _self.textInput.val().split('\n');
  446. var linesNew = [];
  447. var textInputWidth = _self.textInput.width() - 2;
  448. var width = 0;
  449. var lastj = 0;
  450. for(var i=0, ii=lines.length; i<ii; i++)
  451. {
  452. _self.textCalc.html('');
  453. lastj = 0;
  454. for(var j=0, jj=lines[0].length; j<jj; j++)
  455. {
  456. width = _self.textCalc.append(lines[i][j]).width();
  457. if(width > textInputWidth)
  458. {
  459. linesNew.push(lines[i].substring(lastj,j));
  460. lastj = j;
  461. _self.textCalc.html(lines[i][j]);
  462. }
  463. }
  464. if(lastj != j) linesNew.push(lines[i].substring(lastj,j));
  465. }
  466. lines = _self.textInput.val(linesNew.join('\n')).val().split('\n');
  467. var offset = _self.textInput.position();
  468. var left = offset.left;
  469. var top = offset.top;
  470. var underlineOffset = 0;
  471. for(var i=0, ii=lines.length; i<ii; i++)
  472. {
  473. _self.ctx.fillStyle = _self.settings.fillStyle;
  474. _self.ctx.textBaseline = 'top';
  475. _self.ctx.font = fontString;
  476. _self.ctx.fillText(lines[i], left, top);
  477. top += _self.settings.fontSize;
  478. if(lines[i] != '' && _self.settings.fontTypeUnderline)
  479. {
  480. width = _self.textCalc.html(lines[i]).width();
  481. //manually set pixels for underline since to avoid antialiasing 1px issue, and lack of support for underline in canvas
  482. var imgData = _self.ctx.getImageData(0, top+underlineOffset, width, 1);
  483. for (j=0; j<imgData.width*imgData.height*4; j+=4)
  484. {
  485. imgData.data[j] = parseInt(_self.settings.fillStyle.substring(1,3), 16);
  486. imgData.data[j+1] = parseInt(_self.settings.fillStyle.substring(3,5), 16);
  487. imgData.data[j+2] = parseInt(_self.settings.fillStyle.substring(5,7), 16);
  488. imgData.data[j+3] = 255;
  489. }
  490. _self.ctx.putImageData(imgData, left, top+underlineOffset);
  491. }
  492. }
  493. },
  494. /*******************************************************************************
  495. * eraser
  496. *******************************************************************************/
  497. drawEraserDown: function(e, _self)
  498. {
  499. _self.ctx.save();
  500. _self.ctx.globalCompositeOperation = 'destination-out';
  501. _self.drawPencilDown(e, _self);
  502. },
  503. drawEraserMove: function(e, _self)
  504. {
  505. _self.drawPencilMove(e, _self);
  506. },
  507. drawEraserUp: function(e, _self)
  508. {
  509. _self.drawPencilUp(e, _self);
  510. _self.ctx.restore();
  511. },
  512. /*******************************************************************************
  513. * save / load data
  514. *******************************************************************************/
  515. getImage: function()
  516. {
  517. this.canvasSave = document.createElement('canvas');
  518. this.ctxSave = this.canvasSave.getContext('2d');
  519. $(this.canvasSave).css({display:'none', position: 'absolute', left: 0, top: 0}).attr('width', $(this.canvas).attr('width')).attr('height', $(this.canvas).attr('height'));
  520. //if a bg image is set, it will automatically save with the image
  521. if(this.canvasBg) this.ctxSave.drawImage(this.canvasBg, 0, 0);
  522. this.ctxSave.drawImage(this.canvas, 0, 0);
  523. return this.canvasSave.toDataURL();
  524. },
  525. setImage: function(data, addUndo)
  526. {
  527. var _self = this;
  528. var myImage = new Image();
  529. myImage.src = data.toString();
  530. _self.ctx.clearRect(0, 0, _self.canvas.width, _self.canvas.height);
  531. $(myImage).load(function(){
  532. _self.ctx.drawImage(myImage, 0, 0);
  533. if(addUndo) { _self.addUndo(); }
  534. });
  535. },
  536. setBgImage: function(data, addUndo)
  537. {
  538. var _self = this;
  539. var myImage = new Image();
  540. myImage.src = data.toString();
  541. _self.ctxBg.clearRect(0, 0, _self.canvasBg.width, _self.canvasBg.height);
  542. $(myImage).load(function()
  543. {
  544. _self.ctxBg.drawImage(myImage, 0, 0);
  545. });
  546. },
  547. /*******************************************************************************
  548. * undo / redo
  549. *******************************************************************************/
  550. addUndo: function()
  551. {
  552. //if it's not at the end of the array we need to repalce the current array position
  553. if(this.undoCurrent < this.undoArray.length-1)
  554. {
  555. this.undoArray[++this.undoCurrent] = this.getImage();
  556. }
  557. else // owtherwise we push normally here
  558. {
  559. this.undoArray.push(this.getImage());
  560. //if we're at the end of the array we need to slice off the front - in increment required
  561. if(this.undoArray.length > this.undoMax){ this.undoArray = this.undoArray.slice(1, this.undoArray.length); }
  562. //if we're NOT at the end of the array, we just increment
  563. else{ this.undoCurrent++; }
  564. }
  565. //for undo's then a new draw we want to remove everything afterwards - in most cases nothing will happen here
  566. while(this.undoCurrent != this.undoArray.length-1) { this.undoArray.pop(); }
  567. this.undoToggleIcons();
  568. },
  569. setUndoImage: function()
  570. {
  571. this.setImage(this.undoArray[this.undoCurrent]);
  572. },
  573. undoNext: function()
  574. {
  575. if(this.undoArray[this.undoCurrent+1]) { this.undoCurrent++; this.setUndoImage(); }
  576. this.undoToggleIcons();
  577. },
  578. undoPrev: function()
  579. {
  580. if(this.undoArray[this.undoCurrent-1]) { this.undoCurrent--; this.setUndoImage(); }
  581. this.undoToggleIcons();
  582. },
  583. undoToggleIcons: function()
  584. {
  585. var iconUndo = this.mainMenu.menu.find("._wPaint_undo");
  586. var iconRedo = this.mainMenu.menu.find("._wPaint_redo");
  587. if(this.undoCurrent > 0 && this.undoArray.length > 1)
  588. {
  589. if(!iconUndo.hasClass('uactive')) { iconUndo.addClass('uactive'); }
  590. }
  591. else { iconUndo.removeClass('uactive'); }
  592. if(this.undoCurrent < this.undoArray.length-1)
  593. {
  594. if(!iconRedo.hasClass('uactive')) { iconRedo.addClass('uactive'); }
  595. }
  596. else { iconRedo.removeClass('uactive'); }
  597. },
  598. /*******************************************************************************
  599. * Functions
  600. *******************************************************************************/
  601. clearAll: function()
  602. {
  603. this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
  604. this.addUndo();
  605. }
  606. }
  607. /**
  608. * Main Menu
  609. */
  610. function MainMenu(canvas)
  611. {
  612. this.menu = null;
  613. return this;
  614. }
  615. MainMenu.prototype =
  616. {
  617. generate: function(canvas, textMenu)
  618. {
  619. var $canvas = canvas;
  620. this.textMenu = textMenu;
  621. var _self = this;
  622. //setup the line width select
  623. var options = '';
  624. for(var i=$canvas.settings.lineWidthMin; i<=$canvas.settings.lineWidthMax; i++) options += '<option value="' + i + '" ' + ($canvas.settings.lineWidth == i ? 'selected="selected"' : '') + '>' + i + '</option>';
  625. var lineWidth = $('<div class="_wPaint_lineWidth _wPaint_dropDown" title="' + $canvas.settings.menuTitles.lineWidth + '"></div>').append(
  626. $('<select>' + options + '</select>')
  627. .change(function(e){ $canvas.settings.lineWidth = parseInt($(this).val()); })
  628. )
  629. //content
  630. var menuContent = $('<div class="_wPaint_options"></div>');
  631. $.each($canvas.settings.menu, function(i, item)
  632. {
  633. switch(item)
  634. {
  635. case 'undo': menuContent.append($('<div class="_wPaint_icon _wPaint_undo" title="' + $canvas.settings.menuTitles.undo + '"></div>').click(function(){ $canvas.undoPrev(); })); break;
  636. case 'redo': menuContent.append($('<div class="_wPaint_icon _wPaint_redo" title="' + $canvas.settings.menuTitles.redo + '"></div>').click(function(){ $canvas.undoNext(); })); break;
  637. case 'clear': menuContent.append($('<div class="_wPaint_icon _wPaint_clear" title="' + $canvas.settings.menuTitles.clear + '"></div>').click(function(){ $canvas.clearAll(); })); break;
  638. case 'rectangle': menuContent.append($('<div class="_wPaint_icon _wPaint_rectangle" title="' + $canvas.settings.menuTitles.rectangle + '"></div>').click(function(){ _self.set_mode(_self, $canvas, 'Rectangle'); })); break;
  639. case 'ellipse': menuContent.append($('<div class="_wPaint_icon _wPaint_ellipse" title="' + $canvas.settings.menuTitles.ellipse + '"></div>').click(function(){ _self.set_mode(_self, $canvas, 'Ellipse'); })); break;
  640. case 'line': menuContent.append($('<div class="_wPaint_icon _wPaint_line" title="' + $canvas.settings.menuTitles.line + '"></div>').click(function(){ _self.set_mode(_self, $canvas, 'Line'); })); break;
  641. case 'pencil': menuContent.append($('<div class="_wPaint_icon _wPaint_pencil" title="' + $canvas.settings.menuTitles.pencil + '"></div>').click(function(){ _self.set_mode(_self, $canvas, 'Pencil'); })); break;
  642. case 'text': menuContent.append($('<div class="_wPaint_icon _wPaint_text" title="' + $canvas.settings.menuTitles.text + '"></div>').click(function(){ _self.set_mode(_self, $canvas, 'Text'); })); break;
  643. case 'eraser': menuContent.append($('<div class="_wPaint_icon _wPaint_eraser" title="' + $canvas.settings.menuTitles.eraser + '"></div>').click(function(e){ _self.set_mode(_self, $canvas, 'Eraser'); })); break;
  644. case 'fillColor': menuContent.append($('<div class="_wPaint_fillColorPicker _wPaint_colorPicker" title="' + $canvas.settings.menuTitles.fillColor + '"></div>')); break;
  645. case 'lineWidth': menuContent.append(lineWidth); break;
  646. case 'strokeColor': menuContent.append($('<div class="_wPaint_strokeColorPicker _wPaint_colorPicker" title="' + $canvas.settings.menuTitles.strokeColor + 'r"></div>')); break;
  647. }
  648. });
  649. //handle
  650. var menuHandle = $('<div class="_wPaint_handle"></div>')
  651. //get position of canvas
  652. //var offset = $($canvas.canvas).offset();
  653. //menu
  654. return this.menu =
  655. $('<div class="_wPaint_menu _wPaint_menu_' + $canvas.settings.menuOrientation + '"></div>')
  656. .css({position: 'absolute', left: $canvas.settings.menuOffsetX, top: $canvas.settings.menuOffsetY})
  657. .draggable({
  658. handle: menuHandle,
  659. drag: function(){_self.moveTextMenu(_self, _self.textMenu)},
  660. stop: function(){_self.moveTextMenu(_self, _self.textMenu)}
  661. })
  662. .append(menuHandle)
  663. .append(menuContent);
  664. },
  665. moveTextMenu: function(mainMenu, textMenu)
  666. {
  667. if(textMenu.docked)
  668. {
  669. textMenu.menu.css({left: parseInt(mainMenu.menu.css('left')) + textMenu.dockOffsetLeft, top: parseInt(mainMenu.menu.css('top')) + textMenu.dockOffsetTop});
  670. }
  671. },
  672. set_mode: function(_self, $canvas, mode)
  673. {
  674. $canvas.settings.mode = mode;
  675. if(mode == 'Text')
  676. {
  677. _self.textMenu.menu.show();
  678. _self.setWidth($canvas, _self.textMenu.menu);
  679. }
  680. else
  681. {
  682. $canvas.drawTextUp(null, $canvas);
  683. _self.textMenu.menu.hide();
  684. $canvas.textInput.hide();
  685. }
  686. _self.menu.find("._wPaint_icon").removeClass('active');
  687. _self.menu.find("._wPaint_" + mode.toLowerCase()).addClass('active');
  688. },
  689. setWidth: function(canvas, menu)
  690. {
  691. var options = menu.find('._wPaint_options');
  692. if(canvas.settings.menuOrientation === 'vertical')
  693. {
  694. // set proper width
  695. var width = menu.find('._wPaint_options > div:first').outerWidth(true);
  696. width += (options.outerWidth(true) - options.width());
  697. //set proper height
  698. }
  699. else
  700. {
  701. var width = menu.find('._wPaint_handle').outerWidth(true);
  702. width += menu.outerWidth(true) - menu.width();
  703. menu.find('._wPaint_options').children().each(function()
  704. {
  705. width += $(this).outerWidth(true);
  706. });
  707. }
  708. menu.width(width);
  709. }
  710. }
  711. /**
  712. * Text Helper
  713. */
  714. function TextMenu(canvas)
  715. {
  716. this.menu = null;
  717. this.docked = true;
  718. this.dockOffsetLeft = canvas.settings.menuOrientation === 'vertical' ? 36 : 0;
  719. this.dockOffsetTop = canvas.settings.menuOrientation === 'vertical' ? 0 : 36;
  720. return this;
  721. }
  722. TextMenu.prototype =
  723. {
  724. generate: function(canvas, mainMenu)
  725. {
  726. var $canvas = canvas;
  727. var _self = this;
  728. //setup font sizes
  729. var options = '';
  730. for(var i=$canvas.settings.fontSizeMin; i<=$canvas.settings.fontSizeMax; i++) options += '<option value="' + i + '" ' + ($canvas.settings.fontSize == i ? 'selected="selected"' : '') + '>' + i + '</option>';
  731. var fontSize = $('<div class="_wPaint_fontSize _wPaint_dropDown" title="' + $canvas.settings.menuTitles.fontSize + '"></div>').append(
  732. $('<select>' + options + '</select>')
  733. .change(function(e){
  734. var fontSize = parseInt($(this).val());
  735. $canvas.settings.fontSize = fontSize;
  736. $canvas.textInput.css({fontSize:fontSize, lineHeight:fontSize+'px'});
  737. $canvas.textCalc.css({fontSize:fontSize, lineHeight:fontSize+'px'});
  738. })
  739. )
  740. //setup font family
  741. var options = '';
  742. for(var i=0, ii=$canvas.settings.fontFamilyOptions.length; i<ii; i++) options += '<option value="' + $canvas.settings.fontFamilyOptions[i] + '" ' + ($canvas.settings.fontFamily == $canvas.settings.fontFamilyOptions[i] ? 'selected="selected"' : '') + '>' + $canvas.settings.fontFamilyOptions[i] + '</option>';
  743. var fontFamily = $('<div class="_wPaint_fontFamily _wPaint_dropDown" title="' + $canvas.settings.menuTitles.fontFamily + '"><div class="_wPaint_dropDown_cover"></div></div>').append(
  744. $('<select>' + options + '</select>')
  745. .change(function(e){
  746. var fontFamily = $(this).val();
  747. $canvas.settings.fontFamily = fontFamily;
  748. $canvas.textInput.css({fontFamily: fontFamily});
  749. $canvas.textCalc.css({fontFamily: fontFamily});
  750. })
  751. )
  752. //content
  753. var menuContent =
  754. $('<div class="_wPaint_options"></div>')
  755. .append($('<div class="_wPaint_icon _wPaint_bold ' + ($canvas.settings.fontTypeBold ? 'active' : '') + '" title="' + $canvas.settings.menuTitles.bold + '"></div>').click(function(){ _self.setType(_self, $canvas, 'Bold'); }))
  756. .append($('<div class="_wPaint_icon _wPaint_italic ' + ($canvas.settings.fontTypeItalic ? 'active' : '') + '" title="' + $canvas.settings.menuTitles.italic + '"></div>').click(function(){ _self.setType(_self, $canvas, 'Italic'); }))
  757. .append($('<div class="_wPaint_icon _wPaint_underline ' + ($canvas.settings.fontTypeUnderline ? 'active' : '') + '" title="' + $canvas.settings.menuTitles.underline + '"></div>').click(function(){ _self.setType(_self, $canvas, 'Underline'); }))
  758. .append(fontSize)
  759. .append(fontFamily);
  760. //handle
  761. var menuHandle = $('<div class="_wPaint_handle"></div>')
  762. //get position of canvas
  763. var offset = $($canvas.canvas).offset();
  764. //menu
  765. return this.menu =
  766. $('<div class="_wPaint_menu _wPaint_menu_' + $canvas.settings.menuOrientation + '""></div>')
  767. .css({display: 'none', position: 'absolute'})
  768. .draggable({
  769. snap: '._wPaint_menu',
  770. handle: menuHandle,
  771. stop: function(){
  772. $.each($(this).data('draggable').snapElements, function(index, element){
  773. _self.dockOffsetLeft = _self.menu.offset().left - mainMenu.menu.offset().left;
  774. _self.dockOffsetTop = _self.menu.offset().top - mainMenu.menu.offset().top;
  775. _self.docked = element.snapping;
  776. });
  777. }
  778. })
  779. .append(menuHandle)
  780. .append(menuContent);
  781. },
  782. setType: function(_self, $canvas, mode)
  783. {
  784. var element = _self.menu.find("._wPaint_" + mode.toLowerCase());
  785. var isActive = element.hasClass('active')
  786. $canvas.settings['fontType' + mode] = !isActive;
  787. isActive ? element.removeClass('active') : element.addClass('active');
  788. fontTypeBold = $canvas.settings.fontTypeBold ? 'bold' : 'normal';
  789. fontTypeItalic = $canvas.settings.fontTypeItalic ? 'italic' : 'normal';
  790. fontTypeUnderline = $canvas.settings.fontTypeUnderline ? 'underline' : 'none';
  791. $canvas.textInput.css({fontWeight: fontTypeBold}); $canvas.textCalc.css({fontWeight: fontTypeBold});
  792. $canvas.textInput.css({fontStyle: fontTypeItalic}); $canvas.textCalc.css({fontStyle: fontTypeItalic});
  793. $canvas.textInput.css({textDecoration: fontTypeUnderline}); $canvas.textCalc.css({textDecoration: fontTypeUnderline});
  794. }
  795. }
  796. })(jQuery);