PageRenderTime 59ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/BlogEngine/BlogEngine.NET/admin/FileManager/JCrop/js/jquery.Jcrop.js

#
JavaScript | 1600 lines | 1312 code | 130 blank | 158 comment | 170 complexity | 3dfbbf4258f3a51892413942e485ac7a MD5 | raw file
Possible License(s): LGPL-2.1, Apache-2.0, BSD-3-Clause
  1. /**
  2. * jquery.Jcrop.js v0.9.9
  3. * jQuery Image Cropping Plugin
  4. * @author Kelly Hallman <khallman@gmail.com>
  5. * Copyright (c) 2008-2011 Kelly Hallman - released under MIT License {{{
  6. *
  7. * Permission is hereby granted, free of charge, to any person
  8. * obtaining a copy of this software and associated documentation
  9. * files (the "Software"), to deal in the Software without
  10. * restriction, including without limitation the rights to use,
  11. * copy, modify, merge, publish, distribute, sublicense, and/or sell
  12. * copies of the Software, and to permit persons to whom the
  13. * Software is furnished to do so, subject to the following
  14. * conditions:
  15. *
  16. * The above copyright notice and this permission notice shall be
  17. * included in all copies or substantial portions of the Software.
  18. *
  19. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  20. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  21. * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  22. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  23. * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  24. * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  25. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  26. * OTHER DEALINGS IN THE SOFTWARE.
  27. *
  28. * }}}
  29. */
  30. (function ($) {
  31. $.Jcrop = function (obj, opt) {
  32. var options = $.extend({}, $.Jcrop.defaults),
  33. docOffset, lastcurs, ie6mode = false;
  34. // Internal Methods {{{
  35. function px(n) {
  36. return parseInt(n, 10) + 'px';
  37. }
  38. function pct(n) {
  39. return parseInt(n, 10) + '%';
  40. }
  41. function cssClass(cl) {
  42. return options.baseClass + '-' + cl;
  43. }
  44. function supportsColorFade() {
  45. return $.fx.step.hasOwnProperty('backgroundColor');
  46. }
  47. function getPos(obj) //{{{
  48. {
  49. // Updated in v0.9.4 to use built-in dimensions plugin
  50. var pos = $(obj).offset();
  51. return [pos.left, pos.top];
  52. }
  53. //}}}
  54. function mouseAbs(e) //{{{
  55. {
  56. return [(e.pageX - docOffset[0]), (e.pageY - docOffset[1])];
  57. }
  58. //}}}
  59. function setOptions(opt) //{{{
  60. {
  61. if (typeof(opt) !== 'object') {
  62. opt = {};
  63. }
  64. options = $.extend(options, opt);
  65. if (typeof(options.onChange) !== 'function') {
  66. options.onChange = function () {};
  67. }
  68. if (typeof(options.onSelect) !== 'function') {
  69. options.onSelect = function () {};
  70. }
  71. if (typeof(options.onRelease) !== 'function') {
  72. options.onRelease = function () {};
  73. }
  74. }
  75. //}}}
  76. function myCursor(type) //{{{
  77. {
  78. if (type !== lastcurs) {
  79. Tracker.setCursor(type);
  80. lastcurs = type;
  81. }
  82. }
  83. //}}}
  84. function startDragMode(mode, pos) //{{{
  85. {
  86. docOffset = getPos($img);
  87. Tracker.setCursor(mode === 'move' ? mode : mode + '-resize');
  88. if (mode === 'move') {
  89. return Tracker.activateHandlers(createMover(pos), doneSelect);
  90. }
  91. var fc = Coords.getFixed();
  92. var opp = oppLockCorner(mode);
  93. var opc = Coords.getCorner(oppLockCorner(opp));
  94. Coords.setPressed(Coords.getCorner(opp));
  95. Coords.setCurrent(opc);
  96. Tracker.activateHandlers(dragmodeHandler(mode, fc), doneSelect);
  97. }
  98. //}}}
  99. function dragmodeHandler(mode, f) //{{{
  100. {
  101. return function (pos) {
  102. if (!options.aspectRatio) {
  103. switch (mode) {
  104. case 'e':
  105. pos[1] = f.y2;
  106. break;
  107. case 'w':
  108. pos[1] = f.y2;
  109. break;
  110. case 'n':
  111. pos[0] = f.x2;
  112. break;
  113. case 's':
  114. pos[0] = f.x2;
  115. break;
  116. }
  117. } else {
  118. switch (mode) {
  119. case 'e':
  120. pos[1] = f.y + 1;
  121. break;
  122. case 'w':
  123. pos[1] = f.y + 1;
  124. break;
  125. case 'n':
  126. pos[0] = f.x + 1;
  127. break;
  128. case 's':
  129. pos[0] = f.x + 1;
  130. break;
  131. }
  132. }
  133. Coords.setCurrent(pos);
  134. Selection.update();
  135. };
  136. }
  137. //}}}
  138. function createMover(pos) //{{{
  139. {
  140. var lloc = pos;
  141. KeyManager.watchKeys();
  142. return function (pos) {
  143. Coords.moveOffset([pos[0] - lloc[0], pos[1] - lloc[1]]);
  144. lloc = pos;
  145. Selection.update();
  146. };
  147. }
  148. //}}}
  149. function oppLockCorner(ord) //{{{
  150. {
  151. switch (ord) {
  152. case 'n':
  153. return 'sw';
  154. case 's':
  155. return 'nw';
  156. case 'e':
  157. return 'nw';
  158. case 'w':
  159. return 'ne';
  160. case 'ne':
  161. return 'sw';
  162. case 'nw':
  163. return 'se';
  164. case 'se':
  165. return 'nw';
  166. case 'sw':
  167. return 'ne';
  168. }
  169. }
  170. //}}}
  171. function createDragger(ord) //{{{
  172. {
  173. return function (e) {
  174. if (options.disabled) {
  175. return false;
  176. }
  177. if ((ord === 'move') && !options.allowMove) {
  178. return false;
  179. }
  180. btndown = true;
  181. startDragMode(ord, mouseAbs(e));
  182. e.stopPropagation();
  183. e.preventDefault();
  184. return false;
  185. };
  186. }
  187. //}}}
  188. function presize($obj, w, h) //{{{
  189. {
  190. var nw = $obj.width(),
  191. nh = $obj.height();
  192. if ((nw > w) && w > 0) {
  193. nw = w;
  194. nh = (w / $obj.width()) * $obj.height();
  195. }
  196. if ((nh > h) && h > 0) {
  197. nh = h;
  198. nw = (h / $obj.height()) * $obj.width();
  199. }
  200. xscale = $obj.width() / nw;
  201. yscale = $obj.height() / nh;
  202. $obj.width(nw).height(nh);
  203. }
  204. //}}}
  205. function unscale(c) //{{{
  206. {
  207. return {
  208. x: parseInt(c.x * xscale, 10),
  209. y: parseInt(c.y * yscale, 10),
  210. x2: parseInt(c.x2 * xscale, 10),
  211. y2: parseInt(c.y2 * yscale, 10),
  212. w: parseInt(c.w * xscale, 10),
  213. h: parseInt(c.h * yscale, 10)
  214. };
  215. }
  216. //}}}
  217. function doneSelect(pos) //{{{
  218. {
  219. var c = Coords.getFixed();
  220. if ((c.w > options.minSelect[0]) && (c.h > options.minSelect[1])) {
  221. Selection.enableHandles();
  222. Selection.done();
  223. } else {
  224. Selection.release();
  225. }
  226. Tracker.setCursor(options.allowSelect ? 'crosshair' : 'default');
  227. }
  228. //}}}
  229. function newSelection(e) //{{{
  230. {
  231. if (options.disabled) {
  232. return false;
  233. }
  234. if (!options.allowSelect) {
  235. return false;
  236. }
  237. btndown = true;
  238. docOffset = getPos($img);
  239. Selection.disableHandles();
  240. myCursor('crosshair');
  241. var pos = mouseAbs(e);
  242. Coords.setPressed(pos);
  243. Selection.update();
  244. Tracker.activateHandlers(selectDrag, doneSelect);
  245. KeyManager.watchKeys();
  246. e.stopPropagation();
  247. e.preventDefault();
  248. return false;
  249. }
  250. //}}}
  251. function selectDrag(pos) //{{{
  252. {
  253. Coords.setCurrent(pos);
  254. Selection.update();
  255. }
  256. //}}}
  257. function newTracker() //{{{
  258. {
  259. var trk = $('<div></div>').addClass(cssClass('tracker'));
  260. if ($.browser.msie) {
  261. trk.css({
  262. opacity: 0,
  263. backgroundColor: 'white'
  264. });
  265. }
  266. return trk;
  267. }
  268. //}}}
  269. // }}}
  270. // Initialization {{{
  271. // Sanitize some options {{{
  272. if ($.browser.msie && ($.browser.version.split('.')[0] === '6')) {
  273. ie6mode = true;
  274. }
  275. if (typeof(obj) !== 'object') {
  276. obj = $(obj)[0];
  277. }
  278. if (typeof(opt) !== 'object') {
  279. opt = {};
  280. }
  281. // }}}
  282. setOptions(opt);
  283. // Initialize some jQuery objects {{{
  284. // The values are SET on the image(s) for the interface
  285. // If the original image has any of these set, they will be reset
  286. // However, if you destroy() the Jcrop instance the original image's
  287. // character in the DOM will be as you left it.
  288. var img_css = {
  289. border: 'none',
  290. margin: 0,
  291. padding: 0,
  292. position: 'absolute'
  293. };
  294. var $origimg = $(obj);
  295. var $img = $origimg.clone().removeAttr('id').css(img_css);
  296. $img.width($origimg.width());
  297. $img.height($origimg.height());
  298. $origimg.after($img).hide();
  299. presize($img, options.boxWidth, options.boxHeight);
  300. var boundx = $img.width(),
  301. boundy = $img.height(),
  302. $div = $('<div />').width(boundx).height(boundy).addClass(cssClass('holder')).css({
  303. position: 'relative',
  304. backgroundColor: options.bgColor
  305. }).insertAfter($origimg).append($img);
  306. delete(options.bgColor);
  307. if (options.addClass) {
  308. $div.addClass(options.addClass);
  309. }
  310. var $img2 = $('<img />')
  311. .attr('src', $img.attr('src')).css(img_css).width(boundx).height(boundy),
  312. $img_holder = $('<div />')
  313. .width(pct(100)).height(pct(100)).css({
  314. zIndex: 310,
  315. position: 'absolute',
  316. overflow: 'hidden'
  317. }).append($img2),
  318. $hdl_holder = $('<div />')
  319. .width(pct(100)).height(pct(100)).css('zIndex', 320),
  320. $sel = $('<div />')
  321. .css({
  322. position: 'absolute',
  323. zIndex: 300
  324. }).insertBefore($img).append($img_holder, $hdl_holder);
  325. if (ie6mode) {
  326. $sel.css({
  327. overflowY: 'hidden'
  328. });
  329. }
  330. var bound = options.boundary;
  331. var $trk = newTracker().width(boundx + (bound * 2)).height(boundy + (bound * 2)).css({
  332. position: 'absolute',
  333. top: px(-bound),
  334. left: px(-bound),
  335. zIndex: 290
  336. }).mousedown(newSelection);
  337. /* }}} */
  338. // Set more variables {{{
  339. var bgopacity = options.bgOpacity,
  340. xlimit, ylimit, xmin, ymin, xscale, yscale, enabled = true,
  341. btndown, animating, shift_down;
  342. docOffset = getPos($img);
  343. // }}}
  344. // }}}
  345. // Internal Modules {{{
  346. // Touch Module {{{
  347. var Touch = (function () {
  348. // Touch support detection function adapted (under MIT License)
  349. // from code by Jeffrey Sambells - http://github.com/iamamused/
  350. function hasTouchSupport() {
  351. var support = {},
  352. events = ['touchstart', 'touchmove', 'touchend'],
  353. el = document.createElement('div'), i;
  354. try {
  355. for(i=0; i<events.length; i++) {
  356. var eventName = events[i];
  357. eventName = 'on' + eventName;
  358. var isSupported = (eventName in el);
  359. if (!isSupported) {
  360. el.setAttribute(eventName, 'return;');
  361. isSupported = typeof el[eventName] == 'function';
  362. }
  363. support[events[i]] = isSupported;
  364. }
  365. return support.touchstart && support.touchend && support.touchmove;
  366. }
  367. catch(err) {
  368. return false;
  369. }
  370. }
  371. function detectSupport() {
  372. if ((options.touchSupport === true) || (options.touchSupport === false)) return options.touchSupport;
  373. else return hasTouchSupport();
  374. }
  375. return {
  376. createDragger: function (ord) {
  377. return function (e) {
  378. e.pageX = e.originalEvent.changedTouches[0].pageX;
  379. e.pageY = e.originalEvent.changedTouches[0].pageY;
  380. if (options.disabled) {
  381. return false;
  382. }
  383. if ((ord === 'move') && !options.allowMove) {
  384. return false;
  385. }
  386. btndown = true;
  387. startDragMode(ord, mouseAbs(e));
  388. e.stopPropagation();
  389. e.preventDefault();
  390. return false;
  391. };
  392. },
  393. newSelection: function (e) {
  394. e.pageX = e.originalEvent.changedTouches[0].pageX;
  395. e.pageY = e.originalEvent.changedTouches[0].pageY;
  396. return newSelection(e);
  397. },
  398. isSupported: hasTouchSupport,
  399. support: detectSupport()
  400. };
  401. }());
  402. // }}}
  403. // Coords Module {{{
  404. var Coords = (function () {
  405. var x1 = 0,
  406. y1 = 0,
  407. x2 = 0,
  408. y2 = 0,
  409. ox, oy;
  410. function setPressed(pos) //{{{
  411. {
  412. pos = rebound(pos);
  413. x2 = x1 = pos[0];
  414. y2 = y1 = pos[1];
  415. }
  416. //}}}
  417. function setCurrent(pos) //{{{
  418. {
  419. pos = rebound(pos);
  420. ox = pos[0] - x2;
  421. oy = pos[1] - y2;
  422. x2 = pos[0];
  423. y2 = pos[1];
  424. }
  425. //}}}
  426. function getOffset() //{{{
  427. {
  428. return [ox, oy];
  429. }
  430. //}}}
  431. function moveOffset(offset) //{{{
  432. {
  433. var ox = offset[0],
  434. oy = offset[1];
  435. if (0 > x1 + ox) {
  436. ox -= ox + x1;
  437. }
  438. if (0 > y1 + oy) {
  439. oy -= oy + y1;
  440. }
  441. if (boundy < y2 + oy) {
  442. oy += boundy - (y2 + oy);
  443. }
  444. if (boundx < x2 + ox) {
  445. ox += boundx - (x2 + ox);
  446. }
  447. x1 += ox;
  448. x2 += ox;
  449. y1 += oy;
  450. y2 += oy;
  451. }
  452. //}}}
  453. function getCorner(ord) //{{{
  454. {
  455. var c = getFixed();
  456. switch (ord) {
  457. case 'ne':
  458. return [c.x2, c.y];
  459. case 'nw':
  460. return [c.x, c.y];
  461. case 'se':
  462. return [c.x2, c.y2];
  463. case 'sw':
  464. return [c.x, c.y2];
  465. }
  466. }
  467. //}}}
  468. function getFixed() //{{{
  469. {
  470. if (!options.aspectRatio) {
  471. return getRect();
  472. }
  473. // This function could use some optimization I think...
  474. var aspect = options.aspectRatio,
  475. min_x = options.minSize[0] / xscale,
  476. //min_y = options.minSize[1]/yscale,
  477. max_x = options.maxSize[0] / xscale,
  478. max_y = options.maxSize[1] / yscale,
  479. rw = x2 - x1,
  480. rh = y2 - y1,
  481. rwa = Math.abs(rw),
  482. rha = Math.abs(rh),
  483. real_ratio = rwa / rha,
  484. xx, yy;
  485. if (max_x === 0) {
  486. max_x = boundx * 10;
  487. }
  488. if (max_y === 0) {
  489. max_y = boundy * 10;
  490. }
  491. if (real_ratio < aspect) {
  492. yy = y2;
  493. w = rha * aspect;
  494. xx = rw < 0 ? x1 - w : w + x1;
  495. if (xx < 0) {
  496. xx = 0;
  497. h = Math.abs((xx - x1) / aspect);
  498. yy = rh < 0 ? y1 - h : h + y1;
  499. } else if (xx > boundx) {
  500. xx = boundx;
  501. h = Math.abs((xx - x1) / aspect);
  502. yy = rh < 0 ? y1 - h : h + y1;
  503. }
  504. } else {
  505. xx = x2;
  506. h = rwa / aspect;
  507. yy = rh < 0 ? y1 - h : y1 + h;
  508. if (yy < 0) {
  509. yy = 0;
  510. w = Math.abs((yy - y1) * aspect);
  511. xx = rw < 0 ? x1 - w : w + x1;
  512. } else if (yy > boundy) {
  513. yy = boundy;
  514. w = Math.abs(yy - y1) * aspect;
  515. xx = rw < 0 ? x1 - w : w + x1;
  516. }
  517. }
  518. // Magic %-)
  519. if (xx > x1) { // right side
  520. if (xx - x1 < min_x) {
  521. xx = x1 + min_x;
  522. } else if (xx - x1 > max_x) {
  523. xx = x1 + max_x;
  524. }
  525. if (yy > y1) {
  526. yy = y1 + (xx - x1) / aspect;
  527. } else {
  528. yy = y1 - (xx - x1) / aspect;
  529. }
  530. } else if (xx < x1) { // left side
  531. if (x1 - xx < min_x) {
  532. xx = x1 - min_x;
  533. } else if (x1 - xx > max_x) {
  534. xx = x1 - max_x;
  535. }
  536. if (yy > y1) {
  537. yy = y1 + (x1 - xx) / aspect;
  538. } else {
  539. yy = y1 - (x1 - xx) / aspect;
  540. }
  541. }
  542. if (xx < 0) {
  543. x1 -= xx;
  544. xx = 0;
  545. } else if (xx > boundx) {
  546. x1 -= xx - boundx;
  547. xx = boundx;
  548. }
  549. if (yy < 0) {
  550. y1 -= yy;
  551. yy = 0;
  552. } else if (yy > boundy) {
  553. y1 -= yy - boundy;
  554. yy = boundy;
  555. }
  556. return makeObj(flipCoords(x1, y1, xx, yy));
  557. }
  558. //}}}
  559. function rebound(p) //{{{
  560. {
  561. if (p[0] < 0) {
  562. p[0] = 0;
  563. }
  564. if (p[1] < 0) {
  565. p[1] = 0;
  566. }
  567. if (p[0] > boundx) {
  568. p[0] = boundx;
  569. }
  570. if (p[1] > boundy) {
  571. p[1] = boundy;
  572. }
  573. return [p[0], p[1]];
  574. }
  575. //}}}
  576. function flipCoords(x1, y1, x2, y2) //{{{
  577. {
  578. var xa = x1,
  579. xb = x2,
  580. ya = y1,
  581. yb = y2;
  582. if (x2 < x1) {
  583. xa = x2;
  584. xb = x1;
  585. }
  586. if (y2 < y1) {
  587. ya = y2;
  588. yb = y1;
  589. }
  590. return [Math.round(xa), Math.round(ya), Math.round(xb), Math.round(yb)];
  591. }
  592. //}}}
  593. function getRect() //{{{
  594. {
  595. var xsize = x2 - x1,
  596. ysize = y2 - y1,
  597. delta;
  598. if (xlimit && (Math.abs(xsize) > xlimit)) {
  599. x2 = (xsize > 0) ? (x1 + xlimit) : (x1 - xlimit);
  600. }
  601. if (ylimit && (Math.abs(ysize) > ylimit)) {
  602. y2 = (ysize > 0) ? (y1 + ylimit) : (y1 - ylimit);
  603. }
  604. if (ymin / yscale && (Math.abs(ysize) < ymin / yscale)) {
  605. y2 = (ysize > 0) ? (y1 + ymin / yscale) : (y1 - ymin / yscale);
  606. }
  607. if (xmin / xscale && (Math.abs(xsize) < xmin / xscale)) {
  608. x2 = (xsize > 0) ? (x1 + xmin / xscale) : (x1 - xmin / xscale);
  609. }
  610. if (x1 < 0) {
  611. x2 -= x1;
  612. x1 -= x1;
  613. }
  614. if (y1 < 0) {
  615. y2 -= y1;
  616. y1 -= y1;
  617. }
  618. if (x2 < 0) {
  619. x1 -= x2;
  620. x2 -= x2;
  621. }
  622. if (y2 < 0) {
  623. y1 -= y2;
  624. y2 -= y2;
  625. }
  626. if (x2 > boundx) {
  627. delta = x2 - boundx;
  628. x1 -= delta;
  629. x2 -= delta;
  630. }
  631. if (y2 > boundy) {
  632. delta = y2 - boundy;
  633. y1 -= delta;
  634. y2 -= delta;
  635. }
  636. if (x1 > boundx) {
  637. delta = x1 - boundy;
  638. y2 -= delta;
  639. y1 -= delta;
  640. }
  641. if (y1 > boundy) {
  642. delta = y1 - boundy;
  643. y2 -= delta;
  644. y1 -= delta;
  645. }
  646. return makeObj(flipCoords(x1, y1, x2, y2));
  647. }
  648. //}}}
  649. function makeObj(a) //{{{
  650. {
  651. return {
  652. x: a[0],
  653. y: a[1],
  654. x2: a[2],
  655. y2: a[3],
  656. w: a[2] - a[0],
  657. h: a[3] - a[1]
  658. };
  659. }
  660. //}}}
  661. return {
  662. flipCoords: flipCoords,
  663. setPressed: setPressed,
  664. setCurrent: setCurrent,
  665. getOffset: getOffset,
  666. moveOffset: moveOffset,
  667. getCorner: getCorner,
  668. getFixed: getFixed
  669. };
  670. }());
  671. //}}}
  672. // Selection Module {{{
  673. var Selection = (function () {
  674. var awake, hdep = 370;
  675. var borders = {};
  676. var handle = {};
  677. var seehandles = false;
  678. var hhs = options.handleOffset;
  679. // Private Methods
  680. function insertBorder(type) //{{{
  681. {
  682. var jq = $('<div />').css({
  683. position: 'absolute',
  684. opacity: options.borderOpacity
  685. }).addClass(cssClass(type));
  686. $img_holder.append(jq);
  687. return jq;
  688. }
  689. //}}}
  690. function dragDiv(ord, zi) //{{{
  691. {
  692. var jq = $('<div />').mousedown(createDragger(ord)).css({
  693. cursor: ord + '-resize',
  694. position: 'absolute',
  695. zIndex: zi
  696. });
  697. if (Touch.support) {
  698. jq.bind('touchstart', Touch.createDragger(ord));
  699. }
  700. $hdl_holder.append(jq);
  701. return jq;
  702. }
  703. //}}}
  704. function insertHandle(ord) //{{{
  705. {
  706. return dragDiv(ord, hdep++).css({
  707. top: px(-hhs + 1),
  708. left: px(-hhs + 1),
  709. opacity: options.handleOpacity
  710. }).addClass(cssClass('handle'));
  711. }
  712. //}}}
  713. function insertDragbar(ord) //{{{
  714. {
  715. var s = options.handleSize,
  716. h = s,
  717. w = s,
  718. t = hhs,
  719. l = hhs;
  720. switch (ord) {
  721. case 'n':
  722. case 's':
  723. w = pct(100);
  724. break;
  725. case 'e':
  726. case 'w':
  727. h = pct(100);
  728. break;
  729. }
  730. return dragDiv(ord, hdep++).width(w).height(h).css({
  731. top: px(-t + 1),
  732. left: px(-l + 1)
  733. });
  734. }
  735. //}}}
  736. function createHandles(li) //{{{
  737. {
  738. var i;
  739. for (i = 0; i < li.length; i++) {
  740. handle[li[i]] = insertHandle(li[i]);
  741. }
  742. }
  743. //}}}
  744. function moveHandles(c) //{{{
  745. {
  746. var midvert = Math.round((c.h / 2) - hhs),
  747. midhoriz = Math.round((c.w / 2) - hhs),
  748. north = -hhs + 1,
  749. west = -hhs + 1,
  750. east = c.w - hhs,
  751. south = c.h - hhs,
  752. x, y;
  753. if (handle.e) {
  754. handle.e.css({
  755. top: px(midvert),
  756. left: px(east)
  757. });
  758. handle.w.css({
  759. top: px(midvert)
  760. });
  761. handle.s.css({
  762. top: px(south),
  763. left: px(midhoriz)
  764. });
  765. handle.n.css({
  766. left: px(midhoriz)
  767. });
  768. }
  769. if (handle.ne) {
  770. handle.ne.css({
  771. left: px(east)
  772. });
  773. handle.se.css({
  774. top: px(south),
  775. left: px(east)
  776. });
  777. handle.sw.css({
  778. top: px(south)
  779. });
  780. }
  781. if (handle.b) {
  782. handle.b.css({
  783. top: px(south)
  784. });
  785. handle.r.css({
  786. left: px(east)
  787. });
  788. }
  789. }
  790. //}}}
  791. function moveto(x, y) //{{{
  792. {
  793. $img2.css({
  794. top: px(-y),
  795. left: px(-x)
  796. });
  797. $sel.css({
  798. top: px(y),
  799. left: px(x)
  800. });
  801. }
  802. //}}}
  803. function resize(w, h) //{{{
  804. {
  805. $sel.width(w).height(h);
  806. }
  807. //}}}
  808. function refresh() //{{{
  809. {
  810. var c = Coords.getFixed();
  811. Coords.setPressed([c.x, c.y]);
  812. Coords.setCurrent([c.x2, c.y2]);
  813. updateVisible();
  814. }
  815. //}}}
  816. // Internal Methods
  817. function updateVisible() //{{{
  818. {
  819. if (awake) {
  820. return update();
  821. }
  822. }
  823. //}}}
  824. function update() //{{{
  825. {
  826. var c = Coords.getFixed();
  827. resize(c.w, c.h);
  828. moveto(c.x, c.y);
  829. /*
  830. options.drawBorders &&
  831. borders.right.css({ left: px(c.w-1) }) &&
  832. borders.bottom.css({ top: px(c.h-1) });
  833. */
  834. if (seehandles) {
  835. moveHandles(c);
  836. }
  837. if (!awake) {
  838. show();
  839. }
  840. options.onChange.call(api, unscale(c));
  841. }
  842. //}}}
  843. function show() //{{{
  844. {
  845. $sel.show();
  846. if (options.bgFade) {
  847. $img.fadeTo(options.fadeTime, bgopacity);
  848. } else {
  849. $img.css('opacity', bgopacity);
  850. }
  851. awake = true;
  852. }
  853. //}}}
  854. function release() //{{{
  855. {
  856. disableHandles();
  857. $sel.hide();
  858. if (options.bgFade) {
  859. $img.fadeTo(options.fadeTime, 1);
  860. } else {
  861. $img.css('opacity', 1);
  862. }
  863. awake = false;
  864. options.onRelease.call(api);
  865. }
  866. //}}}
  867. function showHandles() //{{{
  868. {
  869. if (seehandles) {
  870. moveHandles(Coords.getFixed());
  871. $hdl_holder.show();
  872. }
  873. }
  874. //}}}
  875. function enableHandles() //{{{
  876. {
  877. seehandles = true;
  878. if (options.allowResize) {
  879. moveHandles(Coords.getFixed());
  880. $hdl_holder.show();
  881. return true;
  882. }
  883. }
  884. //}}}
  885. function disableHandles() //{{{
  886. {
  887. seehandles = false;
  888. $hdl_holder.hide();
  889. }
  890. //}}}
  891. function animMode(v) //{{{
  892. {
  893. if (animating === v) {
  894. disableHandles();
  895. } else {
  896. enableHandles();
  897. }
  898. }
  899. //}}}
  900. function done() //{{{
  901. {
  902. animMode(false);
  903. refresh();
  904. }
  905. //}}}
  906. /* Insert draggable elements {{{*/
  907. // Insert border divs for outline
  908. if (options.drawBorders) {
  909. borders = {
  910. top: insertBorder('hline'),
  911. bottom: insertBorder('hline bottom'),
  912. left: insertBorder('vline'),
  913. right: insertBorder('vline right')
  914. };
  915. }
  916. // Insert handles on edges
  917. if (options.dragEdges) {
  918. handle.t = insertDragbar('n');
  919. handle.b = insertDragbar('s');
  920. handle.r = insertDragbar('e');
  921. handle.l = insertDragbar('w');
  922. }
  923. // Insert side and corner handles
  924. if (options.sideHandles) {
  925. createHandles(['n', 's', 'e', 'w']);
  926. }
  927. if (options.cornerHandles) {
  928. createHandles(['sw', 'nw', 'ne', 'se']);
  929. }
  930. //}}}
  931. var $track = newTracker().mousedown(createDragger('move')).css({
  932. cursor: 'move',
  933. position: 'absolute',
  934. zIndex: 360
  935. });
  936. if (Touch.support) {
  937. $track.bind('touchstart.jcrop', Touch.createDragger('move'));
  938. }
  939. $img_holder.append($track);
  940. disableHandles();
  941. return {
  942. updateVisible: updateVisible,
  943. update: update,
  944. release: release,
  945. refresh: refresh,
  946. isAwake: function () {
  947. return awake;
  948. },
  949. setCursor: function (cursor) {
  950. $track.css('cursor', cursor);
  951. },
  952. enableHandles: enableHandles,
  953. enableOnly: function () {
  954. seehandles = true;
  955. },
  956. showHandles: showHandles,
  957. disableHandles: disableHandles,
  958. animMode: animMode,
  959. done: done
  960. };
  961. }());
  962. //}}}
  963. // Tracker Module {{{
  964. var Tracker = (function () {
  965. var onMove = function () {},
  966. onDone = function () {},
  967. trackDoc = options.trackDocument;
  968. function toFront() //{{{
  969. {
  970. $trk.css({
  971. zIndex: 450
  972. });
  973. if (trackDoc) {
  974. $(document)
  975. .bind('mousemove',trackMove)
  976. .bind('mouseup',trackUp);
  977. }
  978. }
  979. //}}}
  980. function toBack() //{{{
  981. {
  982. $trk.css({
  983. zIndex: 290
  984. });
  985. if (trackDoc) {
  986. $(document)
  987. .unbind('mousemove', trackMove)
  988. .unbind('mouseup', trackUp);
  989. }
  990. }
  991. //}}}
  992. function trackMove(e) //{{{
  993. {
  994. onMove(mouseAbs(e));
  995. return false;
  996. }
  997. //}}}
  998. function trackUp(e) //{{{
  999. {
  1000. e.preventDefault();
  1001. e.stopPropagation();
  1002. if (btndown) {
  1003. btndown = false;
  1004. onDone(mouseAbs(e));
  1005. if (Selection.isAwake()) {
  1006. options.onSelect.call(api, unscale(Coords.getFixed()));
  1007. }
  1008. toBack();
  1009. onMove = function () {};
  1010. onDone = function () {};
  1011. }
  1012. return false;
  1013. }
  1014. //}}}
  1015. function activateHandlers(move, done) //{{{
  1016. {
  1017. btndown = true;
  1018. onMove = move;
  1019. onDone = done;
  1020. toFront();
  1021. return false;
  1022. }
  1023. //}}}
  1024. function trackTouchMove(e) //{{{
  1025. {
  1026. e.pageX = e.originalEvent.changedTouches[0].pageX;
  1027. e.pageY = e.originalEvent.changedTouches[0].pageY;
  1028. return trackMove(e);
  1029. }
  1030. //}}}
  1031. function trackTouchEnd(e) //{{{
  1032. {
  1033. e.pageX = e.originalEvent.changedTouches[0].pageX;
  1034. e.pageY = e.originalEvent.changedTouches[0].pageY;
  1035. return trackUp(e);
  1036. }
  1037. //}}}
  1038. function setCursor(t) //{{{
  1039. {
  1040. $trk.css('cursor', t);
  1041. }
  1042. //}}}
  1043. if (Touch.support) {
  1044. $(document)
  1045. .bind('touchmove', trackTouchMove)
  1046. .bind('touchend', trackTouchEnd);
  1047. }
  1048. if (!trackDoc) {
  1049. $trk.mousemove(trackMove).mouseup(trackUp).mouseout(trackUp);
  1050. }
  1051. $img.before($trk);
  1052. return {
  1053. activateHandlers: activateHandlers,
  1054. setCursor: setCursor
  1055. };
  1056. }());
  1057. //}}}
  1058. // KeyManager Module {{{
  1059. var KeyManager = (function () {
  1060. var $keymgr = $('<input type="radio" />').css({
  1061. position: 'fixed',
  1062. left: '-120px',
  1063. width: '12px'
  1064. }),
  1065. $keywrap = $('<div />').css({
  1066. position: 'absolute',
  1067. overflow: 'hidden'
  1068. }).append($keymgr);
  1069. function watchKeys() //{{{
  1070. {
  1071. if (options.keySupport) {
  1072. $keymgr.show();
  1073. $keymgr.focus();
  1074. }
  1075. }
  1076. //}}}
  1077. function onBlur(e) //{{{
  1078. {
  1079. $keymgr.hide();
  1080. }
  1081. //}}}
  1082. function doNudge(e, x, y) //{{{
  1083. {
  1084. if (options.allowMove) {
  1085. Coords.moveOffset([x, y]);
  1086. Selection.updateVisible();
  1087. }
  1088. e.preventDefault();
  1089. e.stopPropagation();
  1090. }
  1091. //}}}
  1092. function parseKey(e) //{{{
  1093. {
  1094. if (e.ctrlKey) {
  1095. return true;
  1096. }
  1097. shift_down = e.shiftKey ? true : false;
  1098. var nudge = shift_down ? 10 : 1;
  1099. switch (e.keyCode) {
  1100. case 37:
  1101. doNudge(e, -nudge, 0);
  1102. break;
  1103. case 39:
  1104. doNudge(e, nudge, 0);
  1105. break;
  1106. case 38:
  1107. doNudge(e, 0, -nudge);
  1108. break;
  1109. case 40:
  1110. doNudge(e, 0, nudge);
  1111. break;
  1112. case 27:
  1113. Selection.release();
  1114. break;
  1115. case 9:
  1116. return true;
  1117. }
  1118. return false;
  1119. }
  1120. //}}}
  1121. if (options.keySupport) {
  1122. $keymgr.keydown(parseKey).blur(onBlur);
  1123. if (ie6mode || !options.fixedSupport) {
  1124. $keymgr.css({
  1125. position: 'absolute',
  1126. left: '-20px'
  1127. });
  1128. $keywrap.append($keymgr).insertBefore($img);
  1129. } else {
  1130. $keymgr.insertBefore($img);
  1131. }
  1132. }
  1133. return {
  1134. watchKeys: watchKeys
  1135. };
  1136. }());
  1137. //}}}
  1138. // }}}
  1139. // API methods {{{
  1140. function setClass(cname) //{{{
  1141. {
  1142. $div.removeClass().addClass(cssClass('holder')).addClass(cname);
  1143. }
  1144. //}}}
  1145. function animateTo(a, callback) //{{{
  1146. {
  1147. var x1 = parseInt(a[0], 10) / xscale,
  1148. y1 = parseInt(a[1], 10) / yscale,
  1149. x2 = parseInt(a[2], 10) / xscale,
  1150. y2 = parseInt(a[3], 10) / yscale;
  1151. if (animating) {
  1152. return;
  1153. }
  1154. var animto = Coords.flipCoords(x1, y1, x2, y2),
  1155. c = Coords.getFixed(),
  1156. initcr = [c.x, c.y, c.x2, c.y2],
  1157. animat = initcr,
  1158. interv = options.animationDelay,
  1159. ix1 = animto[0] - initcr[0],
  1160. iy1 = animto[1] - initcr[1],
  1161. ix2 = animto[2] - initcr[2],
  1162. iy2 = animto[3] - initcr[3],
  1163. pcent = 0,
  1164. velocity = options.swingSpeed;
  1165. x = animat[0];
  1166. y = animat[1];
  1167. x2 = animat[2];
  1168. y2 = animat[3];
  1169. Selection.animMode(true);
  1170. var anim_timer;
  1171. function queueAnimator() {
  1172. window.setTimeout(animator, interv);
  1173. }
  1174. var animator = (function () {
  1175. return function () {
  1176. pcent += (100 - pcent) / velocity;
  1177. animat[0] = x + ((pcent / 100) * ix1);
  1178. animat[1] = y + ((pcent / 100) * iy1);
  1179. animat[2] = x2 + ((pcent / 100) * ix2);
  1180. animat[3] = y2 + ((pcent / 100) * iy2);
  1181. if (pcent >= 99.8) {
  1182. pcent = 100;
  1183. }
  1184. if (pcent < 100) {
  1185. setSelectRaw(animat);
  1186. queueAnimator();
  1187. } else {
  1188. Selection.done();
  1189. if (typeof(callback) === 'function') {
  1190. callback.call(api);
  1191. }
  1192. }
  1193. };
  1194. }());
  1195. queueAnimator();
  1196. }
  1197. //}}}
  1198. function setSelect(rect) //{{{
  1199. {
  1200. setSelectRaw([
  1201. parseInt(rect[0], 10) / xscale, parseInt(rect[1], 10) / yscale, parseInt(rect[2], 10) / xscale, parseInt(rect[3], 10) / yscale]);
  1202. }
  1203. //}}}
  1204. function setSelectRaw(l) //{{{
  1205. {
  1206. Coords.setPressed([l[0], l[1]]);
  1207. Coords.setCurrent([l[2], l[3]]);
  1208. Selection.update();
  1209. }
  1210. //}}}
  1211. function tellSelect() //{{{
  1212. {
  1213. return unscale(Coords.getFixed());
  1214. }
  1215. //}}}
  1216. function tellScaled() //{{{
  1217. {
  1218. return Coords.getFixed();
  1219. }
  1220. //}}}
  1221. function setOptionsNew(opt) //{{{
  1222. {
  1223. setOptions(opt);
  1224. interfaceUpdate();
  1225. }
  1226. //}}}
  1227. function disableCrop() //{{{
  1228. {
  1229. options.disabled = true;
  1230. Selection.disableHandles();
  1231. Selection.setCursor('default');
  1232. Tracker.setCursor('default');
  1233. }
  1234. //}}}
  1235. function enableCrop() //{{{
  1236. {
  1237. options.disabled = false;
  1238. interfaceUpdate();
  1239. }
  1240. //}}}
  1241. function cancelCrop() //{{{
  1242. {
  1243. Selection.done();
  1244. Tracker.activateHandlers(null, null);
  1245. }
  1246. //}}}
  1247. function destroy() //{{{
  1248. {
  1249. $div.remove();
  1250. $origimg.show();
  1251. $(obj).removeData('Jcrop');
  1252. }
  1253. //}}}
  1254. function setImage(src, callback) //{{{
  1255. {
  1256. Selection.release();
  1257. disableCrop();
  1258. var img = new Image();
  1259. img.onload = function () {
  1260. var iw = img.width;
  1261. var ih = img.height;
  1262. var bw = options.boxWidth;
  1263. var bh = options.boxHeight;
  1264. $img.width(iw).height(ih);
  1265. $img.attr('src', src);
  1266. $img2.attr('src', src);
  1267. presize($img, bw, bh);
  1268. boundx = $img.width();
  1269. boundy = $img.height();
  1270. $img2.width(boundx).height(boundy);
  1271. $trk.width(boundx + (bound * 2)).height(boundy + (bound * 2));
  1272. $div.width(boundx).height(boundy);
  1273. enableCrop();
  1274. if (typeof(callback) === 'function') {
  1275. callback.call(api);
  1276. }
  1277. };
  1278. img.src = src;
  1279. }
  1280. //}}}
  1281. function interfaceUpdate(alt) //{{{
  1282. // This method tweaks the interface based on options object.
  1283. // Called when options are changed and at end of initialization.
  1284. {
  1285. if (options.allowResize) {
  1286. if (alt) {
  1287. Selection.enableOnly();
  1288. } else {
  1289. Selection.enableHandles();
  1290. }
  1291. } else {
  1292. Selection.disableHandles();
  1293. }
  1294. Tracker.setCursor(options.allowSelect ? 'crosshair' : 'default');
  1295. Selection.setCursor(options.allowMove ? 'move' : 'default');
  1296. if (options.hasOwnProperty('setSelect')) {
  1297. setSelect(options.setSelect);
  1298. Selection.done();
  1299. delete(options.setSelect);
  1300. }
  1301. if (options.hasOwnProperty('trueSize')) {
  1302. xscale = options.trueSize[0] / boundx;
  1303. yscale = options.trueSize[1] / boundy;
  1304. }
  1305. if (options.hasOwnProperty('bgColor')) {
  1306. if (supportsColorFade() && options.fadeTime) {
  1307. $div.animate({
  1308. backgroundColor: options.bgColor
  1309. }, {
  1310. queue: false,
  1311. duration: options.fadeTime
  1312. });
  1313. } else {
  1314. $div.css('backgroundColor', options.bgColor);
  1315. }
  1316. delete(options.bgColor);
  1317. }
  1318. if (options.hasOwnProperty('bgOpacity')) {
  1319. bgopacity = options.bgOpacity;
  1320. if (Selection.isAwake()) {
  1321. if (options.fadeTime) {
  1322. $img.fadeTo(options.fadeTime, bgopacity);
  1323. } else {
  1324. $div.css('opacity', options.opacity);
  1325. }
  1326. }
  1327. delete(options.bgOpacity);
  1328. }
  1329. xlimit = options.maxSize[0] || 0;
  1330. ylimit = options.maxSize[1] || 0;
  1331. xmin = options.minSize[0] || 0;
  1332. ymin = options.minSize[1] || 0;
  1333. if (options.hasOwnProperty('outerImage')) {
  1334. $img.attr('src', options.outerImage);
  1335. delete(options.outerImage);
  1336. }
  1337. Selection.refresh();
  1338. }
  1339. //}}}
  1340. //}}}
  1341. if (Touch.support) {
  1342. $trk.bind('touchstart', Touch.newSelection);
  1343. }
  1344. $hdl_holder.hide();
  1345. interfaceUpdate(true);
  1346. var api = {
  1347. setImage: setImage,
  1348. animateTo: animateTo,
  1349. setSelect: setSelect,
  1350. setOptions: setOptionsNew,
  1351. tellSelect: tellSelect,
  1352. tellScaled: tellScaled,
  1353. setClass: setClass,
  1354. disable: disableCrop,
  1355. enable: enableCrop,
  1356. cancel: cancelCrop,
  1357. release: Selection.release,
  1358. destroy: destroy,
  1359. focus: KeyManager.watchKeys,
  1360. getBounds: function () {
  1361. return [boundx * xscale, boundy * yscale];
  1362. },
  1363. getWidgetSize: function () {
  1364. return [boundx, boundy];
  1365. },
  1366. getScaleFactor: function () {
  1367. return [xscale, yscale];
  1368. },
  1369. ui: {
  1370. holder: $div,
  1371. selection: $sel
  1372. }
  1373. };
  1374. if ($.browser.msie) {
  1375. $div.bind('selectstart', function () {
  1376. return false;
  1377. });
  1378. }
  1379. $origimg.data('Jcrop', api);
  1380. return api;
  1381. };
  1382. $.fn.Jcrop = function (options, callback) //{{{
  1383. {
  1384. function attachWhenDone(from) //{{{
  1385. {
  1386. var opt = (typeof(options) === 'object') ? options : {};
  1387. var loadsrc = opt.useImg || from.src;
  1388. var img = new Image();
  1389. img.onload = function () {
  1390. function attachJcrop() {
  1391. var api = $.Jcrop(from, opt);
  1392. if (typeof(callback) === 'function') {
  1393. callback.call(api);
  1394. }
  1395. }
  1396. function attachAttempt() {
  1397. if (!img.width || !img.height) {
  1398. window.setTimeout(attachAttempt, 50);
  1399. } else {
  1400. attachJcrop();
  1401. }
  1402. }
  1403. window.setTimeout(attachAttempt, 50);
  1404. };
  1405. img.src = loadsrc;
  1406. }
  1407. //}}}
  1408. // Iterate over each object, attach Jcrop
  1409. this.each(function () {
  1410. // If we've already attached to this object
  1411. if ($(this).data('Jcrop')) {
  1412. // The API can be requested this way (undocumented)
  1413. if (options === 'api') {
  1414. return $(this).data('Jcrop');
  1415. }
  1416. // Otherwise, we just reset the options...
  1417. else {
  1418. $(this).data('Jcrop').setOptions(options);
  1419. }
  1420. }
  1421. // If we haven't been attached, preload and attach
  1422. else {
  1423. attachWhenDone(this);
  1424. }
  1425. });
  1426. // Return "this" so the object is chainable (jQuery-style)
  1427. return this;
  1428. };
  1429. //}}}
  1430. // Global Defaults {{{
  1431. $.Jcrop.defaults = {
  1432. // Basic Settings
  1433. allowSelect: true,
  1434. allowMove: true,
  1435. allowResize: true,
  1436. trackDocument: true,
  1437. // Styling Options
  1438. baseClass: 'jcrop',
  1439. addClass: null,
  1440. bgColor: 'black',
  1441. bgOpacity: 0.6,
  1442. bgFade: false,
  1443. borderOpacity: 0.4,
  1444. handleOpacity: 0.5,
  1445. handleSize: 9,
  1446. handleOffset: 5,
  1447. aspectRatio: 0,
  1448. keySupport: true,
  1449. cornerHandles: true,
  1450. sideHandles: true,
  1451. drawBorders: true,
  1452. dragEdges: true,
  1453. fixedSupport: true,
  1454. touchSupport: null,
  1455. boxWidth: 0,
  1456. boxHeight: 0,
  1457. boundary: 2,
  1458. fadeTime: 400,
  1459. animationDelay: 20,
  1460. swingSpeed: 3,
  1461. minSelect: [0, 0],
  1462. maxSize: [0, 0],
  1463. minSize: [0, 0],
  1464. // Callbacks / Event Handlers
  1465. onChange: function () {},
  1466. onSelect: function () {},
  1467. onRelease: function () {}
  1468. };
  1469. // }}}
  1470. }(jQuery));