jquery.Jcrop.js 43 KB

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