imgmap.js 94 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952
  1. /**
  2. * Image Map Editor (imgmap) - in-browser imagemap editor
  3. * Copyright (C) 2006 - 2008 Adam Maschek (adam.maschek @ gmail.com)
  4. *
  5. * This program is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU General Public License
  7. * as published by the Free Software Foundation; either version 2
  8. * of the License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program; if not, write to the Free Software
  17. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18. */
  19. /**
  20. * @fileoverview
  21. * Online Image Map Editor - main script file.
  22. * This is the main script file of the Online Image Map Editor.
  23. *
  24. * TODO:
  25. * -scriptload race condition fix
  26. * -destroy/cleanup function ?
  27. * -testing highlighter
  28. * -cursor area_mousemove in opera not refreshing quite well - bug reported
  29. * -get rid of memo array
  30. * -highlight which control point is edited in html or form mode
  31. * -more comments, especially on config vars
  32. * -make function names more logical
  33. * - dumpconfig
  34. * -prepare for bad input /poly not properly closed?
  35. * -prepare for % values in coords
  36. * -prepare for default shape http://www.w3.org/TR/html4/struct/objects.html#edef-AREA
  37. *
  38. * @date 26-02-2007 2:24:50
  39. * @author Adam Maschek (adam.maschek(at)gmail.com)
  40. * @copyright
  41. * @version 2.2
  42. *
  43. */
  44. /*jslint browser: true, newcap: false, white: false, onevar: false, plusplus: false, eqeqeq: false, nomen: false */
  45. /*global imgmapStrings:true, window:false, G_vmlCanvasManager:false, air:false, imgmap_spawnObjects:true */
  46. /**
  47. * @author Adam Maschek
  48. * @constructor
  49. * @param config The config object.
  50. */
  51. function imgmap(config) {
  52. /** Version string of imgmap */
  53. this.version = "2.2";
  54. /** Build date of imgmap */
  55. this.buildDate = "2009/08/12 22:18";
  56. /** Sequential build number of imgmap */
  57. this.buildNumber = "113";
  58. /** Config object of the imgmap instance */
  59. this.config = {};
  60. /** Status flag to indicate current drawing mode */
  61. this.is_drawing = 0;
  62. /** Array to hold language strings */
  63. this.strings = [];
  64. /** Helper array for some drawing operations */
  65. this.memory = [];
  66. /** Array to hold reference to all areas (canvases) */
  67. this.areas = [];
  68. /** Array to hold last log entries */
  69. this.logStore = [];
  70. /** Associative array to hold bound event handlers */
  71. this.eventHandlers = {};
  72. this.currentid = 0;
  73. this.draggedId = null;
  74. this.selectedId = null;
  75. this.nextShape = 'rect';
  76. /** possible values: 0 - edit, 1 - preview */
  77. this.viewmode = 0;
  78. /** array of dynamically loaded javascripts */
  79. this.loadedScripts = [];
  80. this.isLoaded = false;
  81. this.cntReloads = 0;
  82. /** holds the name of the actively edited map, use getMapName to read it */
  83. this.mapname = '';
  84. /** holds the id of the actively edited map, use getMapIdto read it */
  85. this.mapid = '';
  86. /** watermark to attach to output */
  87. this.waterMark = '<!-- Created by Online Image Map Editor (http://www.maschek.hu/imagemap/index) -->';
  88. /** global scale of areas (1-normal, 2-doubled, 0.5-half, etc.) */
  89. this.globalscale = 1;
  90. /** is_drawing draw mode constant */
  91. this.DM_RECTANGLE_DRAW = 1;
  92. /** is_drawing draw mode constant */
  93. this.DM_RECTANGLE_MOVE = 11;
  94. /** is_drawing draw mode constant */
  95. this.DM_RECTANGLE_RESIZE_TOP = 12;
  96. /** is_drawing draw mode constant */
  97. this.DM_RECTANGLE_RESIZE_RIGHT = 13;
  98. /** is_drawing draw mode constant */
  99. this.DM_RECTANGLE_RESIZE_BOTTOM = 14;
  100. /** is_drawing draw mode constant */
  101. this.DM_RECTANGLE_RESIZE_LEFT = 15;
  102. /** is_drawing draw mode constant */
  103. this.DM_SQUARE_DRAW = 2;
  104. /** is_drawing draw mode constant */
  105. this.DM_SQUARE_MOVE = 21;
  106. /** is_drawing draw mode constant */
  107. this.DM_SQUARE_RESIZE_TOP = 22;
  108. /** is_drawing draw mode constant */
  109. this.DM_SQUARE_RESIZE_RIGHT = 23;
  110. /** is_drawing draw mode constant */
  111. this.DM_SQUARE_RESIZE_BOTTOM = 24;
  112. /** is_drawing draw mode constant */
  113. this.DM_SQUARE_RESIZE_LEFT = 25;
  114. /** is_drawing draw mode constant */
  115. this.DM_POLYGON_DRAW = 3;
  116. /** is_drawing draw mode constant */
  117. this.DM_POLYGON_LASTDRAW = 30;
  118. /** is_drawing draw mode constant */
  119. this.DM_POLYGON_MOVE = 31;
  120. /** is_drawing draw mode constant */
  121. this.DM_BEZIER_DRAW = 4;
  122. /** is_drawing draw mode constant */
  123. this.DM_BEZIER_LASTDRAW = 40;
  124. /** is_drawing draw mode constant */
  125. this.DM_BEZIER_MOVE = 41;
  126. //set some config defaults below
  127. /**
  128. * Mode of operation
  129. * possible values:
  130. * editor - classical editor,
  131. * editor2 - dreamweaver style editor,
  132. * highlighter - map highlighter, will spawn imgmap instances for each map found in the current page
  133. * highlighter_spawn - internal mode after spawning imgmap objects
  134. */
  135. this.config.mode = "editor";
  136. this.config.baseroot = '';
  137. this.config.lang = '';
  138. this.config.defaultLang = 'en';
  139. this.config.loglevel = 0;
  140. this.config.custom_callbacks = {};//possible values: see below!
  141. /** Callback events that you can handle in your GUI. */
  142. this.event_types = [
  143. 'onModeChanged',
  144. 'onHtmlChanged',
  145. 'onAddArea',
  146. 'onRemoveArea',
  147. 'onDrawArea',
  148. 'onResizeArea',
  149. 'onRelaxArea',
  150. 'onFocusArea',
  151. 'onBlurArea',
  152. 'onMoveArea',
  153. 'onSelectRow',
  154. 'onLoadImage',
  155. 'onSetMap',
  156. 'onGetMap',
  157. 'onSelectArea',
  158. 'onDblClickArea',
  159. 'onStatusMessage',
  160. 'onAreaChanged'];
  161. //default color values
  162. this.config.CL_DRAW_BOX = '#E32636';
  163. this.config.CL_DRAW_SHAPE = '#d00';
  164. this.config.CL_DRAW_BG = '#fff';
  165. this.config.CL_NORM_BOX = '#E32636';
  166. this.config.CL_NORM_SHAPE = '#d00';
  167. this.config.CL_NORM_BG = '#fff';
  168. this.config.CL_HIGHLIGHT_BOX = '#E32636';
  169. this.config.CL_HIGHLIGHT_SHAPE = '#d00';
  170. this.config.CL_HIGHLIGHT_BG = '#fff';
  171. this.config.CL_KNOB = '#555';
  172. this.config.bounding_box = true;
  173. this.config.label = '%n';
  174. //the format string of the area labels - possible values: %n - number, %c - coords, %h - href, %a - alt, %t - title
  175. this.config.label_class = 'imgmap_label';
  176. //the css class to apply on labels
  177. this.config.label_style = 'font: bold 10px Arial';
  178. //this.config.label_style = 'font-weight: bold; font-size: 10px; font-family: Arial; color: #964';
  179. //the css style(s) to apply on labels
  180. this.config.hint = '#%n %h';
  181. //the format string of the area mouseover hints - possible values: %n - number, %c - coords, %h - href, %a - alt, %t - title
  182. this.config.draw_opacity = '35';
  183. //the opacity value of the area while drawing, moving or resizing - possible values 0 - 100 or range "(x)-y"
  184. this.config.norm_opacity = '50';
  185. //the opacity value of the area while relaxed - possible values 0 - 100 or range "(x)-y"
  186. this.config.highlight_opacity = '70';
  187. //the opacity value of the area while highlighted - possible values 0 - 100 or range "(x)-y"
  188. this.config.cursor_default = 'crosshair'; //auto/pointer
  189. //the css cursor while hovering over the image
  190. //browser sniff
  191. var ua = navigator.userAgent;
  192. this.isMSIE = (navigator.appName == "Microsoft Internet Explorer");
  193. this.isMSIE5 = this.isMSIE && (ua.indexOf('MSIE 5') != -1);
  194. this.isMSIE5_0 = this.isMSIE && (ua.indexOf('MSIE 5.0') != -1);
  195. this.isMSIE7 = this.isMSIE && (ua.indexOf('MSIE 7') != -1);
  196. this.isGecko = ua.indexOf('Gecko') != -1;
  197. this.isSafari = ua.indexOf('Safari') != -1;
  198. this.isOpera = (typeof window.opera != 'undefined');
  199. this.setup(config);
  200. }
  201. /**
  202. * Return an object given by id or object itself.
  203. * @date 22-02-2007 0:14:50
  204. * @author Adam Maschek (adam.maschek(at)gmail.com)
  205. * @param objorid A DOM object, or id of a DOM object.
  206. * @return The identified DOM object or null on error.
  207. */
  208. imgmap.prototype.assignOID = function(objorid) {
  209. try {
  210. if (typeof objorid == 'undefined') {
  211. this.log("Undefined object passed to assignOID.");// Called from: " + arguments.callee.caller, 1);
  212. return null;
  213. }
  214. else if (typeof objorid == 'object') {
  215. return objorid;
  216. }
  217. else if (typeof objorid == 'string') {
  218. return document.getElementById(objorid);
  219. }
  220. }
  221. catch (err) {
  222. this.log("Error in assignOID", 1);
  223. }
  224. return null;
  225. };
  226. /**
  227. * Main setup function.
  228. * Can be called manually or constructor will call it.
  229. * @date 22-02-2007 0:15:42
  230. * @author Adam Maschek (adam.maschek(at)gmail.com)
  231. * @param config config object
  232. * @return True if all went ok.
  233. */
  234. imgmap.prototype.setup = function(config) {
  235. //this.log('setup');
  236. //copy non-default config parameters to this.config
  237. for (var i in config) {
  238. if (config.hasOwnProperty(i)) {
  239. this.config[i] = config[i];
  240. }
  241. }
  242. //set document event hooks
  243. this.addEvent(document, 'keydown', this.eventHandlers.doc_keydown = this.doc_keydown.bind(this));
  244. this.addEvent(document, 'keyup', this.eventHandlers.doc_keyup = this.doc_keyup.bind(this));
  245. this.addEvent(document, 'mousedown', this.eventHandlers.doc_mousedown = this.doc_mousedown.bind(this));
  246. //set pic_container element - supposedly it already exists in the DOM
  247. if (config && config.pic_container) {
  248. this.pic_container = this.assignOID(config.pic_container);
  249. this.disableSelection(this.pic_container);
  250. }
  251. if (!this.config.baseroot) {
  252. //search for a base - theoretically there can only be one, but lets search
  253. //for the first non-empty
  254. var bases = document.getElementsByTagName('base');
  255. var base = '';
  256. for (i=0; i<bases.length; i++) {//i declared earlier
  257. if (bases[i].href) {
  258. base = bases[i].href;
  259. //append slash if missing
  260. if (base.charAt(base.length-1) != '/') {
  261. base+= '/';
  262. }
  263. break;
  264. }
  265. }
  266. //search for scripts
  267. var scripts = document.getElementsByTagName('script');
  268. var found = false;
  269. for (i=0; i<scripts.length; i++) {//i declared earlier
  270. if (scripts[i].src && scripts[i].src.match(/imgmap\w*\.js(\?.*?)?$/)) {
  271. var src = scripts[i].src;
  272. //cut filename part, leave last slash
  273. src = src.substring(0, src.lastIndexOf('/') + 1);
  274. //set final baseroot path
  275. if (base && src.indexOf('://') == -1) {
  276. this.config.baseroot = base + src;
  277. }
  278. else {
  279. this.config.baseroot = src;
  280. }
  281. //exit loop
  282. found = true;
  283. break;
  284. }
  285. }
  286. if (!found && this.config.baseroot === '') {
  287. this.log("Unable to detect baseroot.", 1);
  288. }
  289. }
  290. //load excanvas js - as soon as possible
  291. if (this.isMSIE &&
  292. typeof window.CanvasRenderingContext2D == 'undefined' && typeof G_vmlCanvasManager == 'undefined') {
  293. this.loadScript(this.config.baseroot + 'excanvas.js');
  294. //alert('loadcanvas');
  295. }
  296. //alert(this.config.baseroot);
  297. //load language js - as soon as possible
  298. if (!this.config.lang) {
  299. this.config.lang = this.detectLanguage();
  300. }
  301. if (typeof imgmapStrings == 'undefined') {
  302. //language file might have already been loaded (ex highlighter mode)
  303. this.loadScript(this.config.baseroot + 'lang_' + this.config.lang + '.js');
  304. }
  305. //check event hooks
  306. var found, j, le;
  307. for (i in this.config.custom_callbacks) {
  308. if (this.config.custom_callbacks.hasOwnProperty(i)) {
  309. found = false;
  310. for (j = 0, le = this.event_types.length; j < le; j++) {
  311. if (i == this.event_types[j]) {
  312. found = true;
  313. break;
  314. }
  315. }
  316. if (!found) {
  317. this.log("Unknown custom callback: " + i, 1);
  318. }
  319. }
  320. }
  321. //hook onload event - as late as possible
  322. this.addEvent(window, 'load', this.onLoad.bind(this));
  323. return true;
  324. };
  325. /**
  326. * currently unused
  327. * @ignore
  328. */
  329. imgmap.prototype.retryDelayed = function(fn, delay, tries) {
  330. if (typeof fn.tries == 'undefined') {fn.tries = 0;}
  331. //alert(fn.tries+1);
  332. if (fn.tries++ < tries) {
  333. //alert('ss');
  334. window.setTimeout(function() {
  335. fn.apply(this);
  336. }, delay);
  337. }
  338. };
  339. /**
  340. * EVENT HANDLER: Handle event when the page with scripts is loaded.
  341. * @date 22-02-2007 0:16:22
  342. * @author Adam Maschek (adam.maschek(at)gmail.com)
  343. * @param e The event object.
  344. */
  345. imgmap.prototype.onLoad = function(e) {
  346. if (this.isLoaded) {return true;}
  347. var _this = this;
  348. //this.log('readystate: ' + document.readyState);
  349. if (typeof imgmapStrings == 'undefined') {
  350. if (this.cntReloads++ < 5) {
  351. //this.retryDelayed(_this.onLoad(), 1000, 3);
  352. window.setTimeout(function () {_this.onLoad(e);} ,1200);
  353. this.log('Delaying onload (language ' + this.config.lang + ' not loaded, try: ' + this.cntReloads + ')');
  354. return false;
  355. }
  356. else if (this.config.lang != this.config.defaultLang && this.config.defaultLang != 'en') {
  357. this.log('Falling back to default language: ' + this.config.defaultLang);
  358. this.cntReloads = 0;
  359. this.config.lang = this.config.defaultLang;
  360. this.loadScript(this.config.baseroot + 'lang_' + this.config.lang + '.js');
  361. window.setTimeout(function () {_this.onLoad(e);} ,1200);
  362. return false;
  363. }
  364. else if (this.config.lang != 'en') {
  365. this.log('Falling back to english language');
  366. this.cntReloads = 0;
  367. this.config.lang = 'en';
  368. this.loadScript(this.config.baseroot + 'lang_' + this.config.lang + '.js');
  369. window.setTimeout(function () {_this.onLoad(e);} ,1200);
  370. return false;
  371. }
  372. }
  373. //else
  374. try {
  375. this.loadStrings(imgmapStrings);
  376. }
  377. catch (err) {
  378. this.log("Unable to load language strings", 1);
  379. }
  380. //check if ExplorerCanvas correctly loaded - detect if browser supports canvas
  381. //alert(typeof G_vmlCanvasManager + this.isMSIE + typeof window.CanvasRenderingContext2D);
  382. if (this.isMSIE) {
  383. //alert('cccc');
  384. //alert(typeof G_vmlCanvasManager);
  385. if (typeof window.CanvasRenderingContext2D == 'undefined' && typeof G_vmlCanvasManager == 'undefined') {
  386. //alert('bbb');
  387. /*
  388. if (this.cntReloads++ < 5) {
  389. var _this = this;
  390. //this.retryDelayed(_this.onLoad(), 1000, 3);
  391. window.setTimeout(function () {
  392. _this.onLoad(e);
  393. }
  394. ,1000
  395. );
  396. //alert('aaa');
  397. this.log('Delaying onload (excanvas not loaded, try: ' + this.cntReloads + ')');
  398. return false;
  399. }
  400. */
  401. this.log(this.strings.ERR_EXCANVAS_LOAD, 2);//critical error
  402. }
  403. }
  404. if (this.config.mode == 'highlighter') {
  405. //call global scope function
  406. imgmap_spawnObjects(this.config);
  407. }
  408. this.isLoaded = true;
  409. return true;
  410. };
  411. /**
  412. * Attach new 'evt' event handler 'callback' to 'obj'
  413. * @date 24-02-2007 21:16:20
  414. * @param obj The object on which the handler is defined.
  415. * @param evt The name of the event, like mousedown.
  416. * @param callback The callback function (named if you want it to be removed).
  417. */
  418. imgmap.prototype.addEvent = function(obj, evt, callback) {
  419. if (obj.attachEvent) {
  420. //Microsoft style registration model
  421. return obj.attachEvent("on" + evt, callback);
  422. }
  423. else if (obj.addEventListener) {
  424. //W3C style model
  425. obj.addEventListener(evt, callback, false);
  426. return true;
  427. }
  428. else {
  429. obj['on' + evt] = callback;
  430. }
  431. };
  432. /**
  433. * Detach 'evt' event handled by 'callback' from 'obj' object.
  434. * Callback must be a non anonymous function, see eventHandlers.
  435. * @see #eventHandlers
  436. * Example: myimgmap.removeEvent(myimgmap.pic, 'mousedown', myimgmap.eventHandlers.img_mousedown);
  437. * @date 24-11-2007 15:22:17
  438. * @param obj The object on which the handler is defined.
  439. * @param evt The name of the event, like mousedown.
  440. * @param callback The named callback function.
  441. */
  442. imgmap.prototype.removeEvent = function(obj, evt, callback) {
  443. if (obj.detachEvent) {
  444. //Microsoft style detach model
  445. return obj.detachEvent("on" + evt, callback);
  446. }
  447. else if (obj.removeEventListener) {
  448. //W3C style model
  449. obj.removeEventListener(evt, callback, false);
  450. return true;
  451. }
  452. else {
  453. obj['on' + evt] = null;
  454. }
  455. };
  456. /**
  457. * We need this because load events for scripts function slightly differently.
  458. * @link http://dean.edwards.name/weblog/2006/06/again/
  459. * @author Adam Maschek (adam.maschek(at)gmail.com)
  460. * @date 24-03-2007 11:02:21
  461. */
  462. imgmap.prototype.addLoadEvent = function(obj, callback) {
  463. if (obj.attachEvent) {
  464. //Microsoft style registration model
  465. return obj.attachEvent("onreadystatechange", callback);
  466. }
  467. else if (obj.addEventListener) {
  468. //W3C style registration model
  469. obj.addEventListener('load', callback, false);
  470. return true;
  471. }
  472. else {
  473. obj.onload = callback;
  474. }
  475. };
  476. /**
  477. * Include another js script into the current document.
  478. * @date 22-02-2007 0:17:04
  479. * @author Adam Maschek (adam.maschek(at)gmail.com)
  480. * @param url The url of the script we want to load.
  481. * @see #script_load
  482. * @see #addLoadEvent
  483. */
  484. imgmap.prototype.loadScript = function(url) {
  485. if (url === '') {return false;}
  486. if (this.loadedScripts[url] == 1) {return true;}//script already loaded
  487. this.log('Loading script: ' + url);
  488. //we might need this someday for safari?
  489. //var temp = '<script language="javascript" type="text/javascript" src="' + url + '"></script>';
  490. //document.write(temp);
  491. try {
  492. var head = document.getElementsByTagName('head')[0];
  493. var temp = document.createElement('SCRIPT');
  494. temp.setAttribute('language', 'javascript');
  495. temp.setAttribute('type', 'text/javascript');
  496. temp.setAttribute('src', url);
  497. //temp.setAttribute('defer', true);
  498. head.appendChild(temp);
  499. this.addLoadEvent(temp, this.script_load.bind(this));
  500. }
  501. catch (err) {
  502. this.log('Error loading script: ' + url);
  503. }
  504. return true;
  505. };
  506. /**
  507. * EVENT HANDLER: Event handler of external script loaded.
  508. * @param e The event object.
  509. */
  510. imgmap.prototype.script_load = function(e) {
  511. var obj = (this.isMSIE) ? window.event.srcElement : e.currentTarget;
  512. var url = obj.src;
  513. var complete = false;
  514. if (typeof obj.readyState != 'undefined') {
  515. //explorer
  516. if (obj.readyState == 'complete' || obj.readyState == 'loaded') {
  517. complete = true;
  518. }
  519. }
  520. else {
  521. //other browsers?
  522. complete = true;
  523. }
  524. if (complete) {
  525. this.loadedScripts[url] = 1;
  526. this.log('Loaded script: ' + url);
  527. return true;
  528. }
  529. };
  530. /**
  531. * Load strings from a key:value object to the prototype strings array.
  532. * @author adam
  533. * @date 2007
  534. * @param obj Javascript object that holds key:value pairs.
  535. */
  536. imgmap.prototype.loadStrings = function(obj) {
  537. for (var key in obj) {
  538. if (obj.hasOwnProperty(key)) {
  539. this.strings[key] = obj[key];
  540. }
  541. }
  542. };
  543. /**
  544. * This function is to load a given img url to the pic_container.
  545. *
  546. * Loading an image will clear all current maps.
  547. * @see #useImage
  548. * @param img The imageurl or object to load (if object, function will get url, and do a recall)
  549. * @param imgw The width we want to force on the image (optional)
  550. * @param imgh The height we want to force on the image (optional)
  551. * @returns True on success
  552. */
  553. imgmap.prototype.loadImage = function(img, imgw, imgh) {
  554. //test for container
  555. if (typeof this.pic_container == 'undefined') {
  556. this.log('You must have pic_container defined to use loadImage!', 2);
  557. return false;
  558. }
  559. //wipe all
  560. this.removeAllAreas();
  561. //reset scale
  562. this.globalscale = 1;
  563. this.fireEvent('onHtmlChanged', '');//empty
  564. if (!this._getLastArea()) {
  565. //init with one new area if there was none editable
  566. if (this.config.mode != "editor2") {this.addNewArea();}
  567. }
  568. if (typeof img == 'string') {
  569. //there is an image given with url to load
  570. if (typeof this.pic == 'undefined') {
  571. this.pic = document.createElement('IMG');
  572. this.pic_container.appendChild(this.pic);
  573. //event handler hooking - only at the first load
  574. this.addEvent(this.pic, 'mousedown', this.eventHandlers.img_mousedown = this.img_mousedown.bind(this));
  575. this.addEvent(this.pic, 'mouseup', this.eventHandlers.img_mouseup = this.img_mouseup.bind(this));
  576. this.addEvent(this.pic, 'mousemove', this.eventHandlers.img_mousemove = this.img_mousemove.bind(this));
  577. this.pic.style.cursor = this.config.cursor_default;
  578. }
  579. //img ='../../'+img;
  580. //this.log('Loading image: ' + img, 0);
  581. //calculate timestamp to bypass browser cache mechanism
  582. var q = '?';
  583. if (img.indexOf('?') > -1) {
  584. q = '&';
  585. }
  586. this.pic.src = img + q + (new Date().getTime());
  587. if (imgw && imgw > 0) {this.pic.setAttribute('width', imgw);}
  588. if (imgh && imgh > 0) {this.pic.setAttribute('height', imgh);}
  589. this.fireEvent('onLoadImage', this.pic);
  590. return true;
  591. }
  592. else if (typeof img == 'object') {
  593. //we have to use the src of the image object
  594. var src = img.src; //img.getAttribute('src');
  595. if (src === '' && img.getAttribute('mce_src') !== '') {
  596. //if it is a tinymce object, it has no src but mce_src attribute!
  597. src = img.getAttribute('mce_src');
  598. }
  599. else if (src === '' && img.getAttribute('_fcksavedurl') !== '') {
  600. //if it is an fck object, it might have only _fcksavedurl attribute!
  601. src = img.getAttribute('_fcksavedurl');
  602. }
  603. // Get the displayed dimensions of the image
  604. if (!imgw) {
  605. imgw = img.clientWidth;
  606. }
  607. if (!imgh) {
  608. imgh = img.clientHeight;
  609. }
  610. //recurse, this time with the url string
  611. return this.loadImage(src, imgw, imgh);
  612. }
  613. };
  614. /**
  615. * We use this when there is an existing image object we want to handle with imgmap.
  616. * Mainly used in highlighter mode.
  617. * @author adam
  618. * @see #loadImage
  619. * @see #imgmap_spawnObjects
  620. * @date 2007
  621. * @param img DOM object or id of image we want to use.
  622. */
  623. imgmap.prototype.useImage = function(img) {
  624. //wipe all
  625. this.removeAllAreas();
  626. if (!this._getLastArea()) {
  627. //init with one new area if there was none editable
  628. if (this.config.mode != "editor2") {this.addNewArea();}
  629. }
  630. img = this.assignOID(img);
  631. if (typeof img == 'object') {
  632. if (typeof this.pic != 'undefined') {
  633. //remove previous handlers
  634. this.removeEvent(this.pic, 'mousedown', this.eventHandlers.img_mousedown);
  635. this.removeEvent(this.pic, 'mouseup', this.eventHandlers.img_mouseup);
  636. this.removeEvent(this.pic, 'mousemove', this.eventHandlers.img_mousemove);
  637. this.pic.style.cursor = '';
  638. }
  639. this.pic = img;
  640. //hook event handlers
  641. this.addEvent(this.pic, 'mousedown', this.eventHandlers.img_mousedown = this.img_mousedown.bind(this));
  642. this.addEvent(this.pic, 'mouseup', this.eventHandlers.img_mouseup = this.img_mouseup.bind(this));
  643. this.addEvent(this.pic, 'mousemove', this.eventHandlers.img_mousemove = this.img_mousemove.bind(this));
  644. this.pic.style.cursor = this.config.cursor_default;
  645. if (this.pic.parentNode.className == 'pic_container') {
  646. //use existing container
  647. this.pic_container = this.pic.parentNode;
  648. }
  649. else {
  650. //dynamically create container
  651. this.pic_container = document.createElement("div");
  652. this.pic_container.className = 'pic_container';
  653. this.pic.parentNode.insertBefore(this.pic_container, this.pic);
  654. //ref: If the node already exists it is removed from current parent node, then added to new parent node.
  655. this.pic_container.appendChild(this.pic);
  656. }
  657. this.fireEvent('onLoadImage', this.pic);
  658. return true;
  659. }
  660. };
  661. /**
  662. * Fires custom hook onStatusMessage, passing the status string.
  663. * Use this to update your GUI.
  664. * @author Adam Maschek (adam.maschek(at)gmail.com)
  665. * @date 26-07-2008 13:22:54
  666. * @param str The status string
  667. */
  668. imgmap.prototype.statusMessage = function(str) {
  669. this.fireEvent('onStatusMessage', str);
  670. };
  671. /**
  672. * Adds basic logging functionality using firebug console object if available.
  673. * Also tries to use AIR introspector if available.
  674. * @date 20-02-2007 17:55:18
  675. * @author Adam Maschek (adam.maschek(at)gmail.com)
  676. * @param obj The object or string you want to debug/echo.
  677. * @level level The log level, 0 being the smallest issue.
  678. */
  679. imgmap.prototype.log = function(obj, level) {
  680. /*
  681. if (level === '' || typeof level == 'undefined') {level = 0;}
  682. if (this.config.loglevel != -1 && level >= this.config.loglevel) {
  683. this.logStore.push({level: level, obj: obj});
  684. }
  685. if (typeof console == 'object') {
  686. console.log(obj);
  687. }
  688. else if (this.isOpera) {
  689. opera.postError(level + ': ' + obj);
  690. }
  691. else if (typeof air == 'object') {
  692. //we are inside AIR
  693. if (typeof air.Introspector == 'object') {
  694. air.Introspector.Console.log(obj);
  695. }
  696. else {
  697. air.trace(obj);
  698. }
  699. }
  700. else {
  701. if (level > 1) {
  702. //alert(level + ': ' + obj);
  703. //dump with all pevious errors:
  704. var msg = '';
  705. for (var i=0, le = this.logStore.length; i<le; i++) {
  706. msg+= this.logStore[i].level + ': ' + this.logStore[i].obj + "\n";
  707. }
  708. alert(msg);
  709. }
  710. else {
  711. window.defaultStatus = (level + ': ' + obj);
  712. }
  713. }
  714. */
  715. };
  716. /**
  717. * Produces the image map HTML output with the defined areas.
  718. * Invokes getMapInnerHTML.
  719. * @author Adam Maschek (adam.maschek(at)gmail.com)
  720. * @date 2006-06-06 15:10:27
  721. * @param flags Currently ony 'noscale' used to prevent scaling of coordinates in preview mode.
  722. * @return The generated html code.
  723. */
  724. imgmap.prototype.getMapHTML = function(flags) {
  725. var html = '<map id="'+this.getMapId()+'" name="'+this.getMapName()+'">' + this.getMapInnerHTML(flags) + this.waterMark + '</map>';
  726. this.fireEvent('onGetMap', html);
  727. //alert(html);
  728. return html;
  729. };
  730. /**
  731. * Get the map areas part only of the current imagemap.
  732. * @see #getMapHTML
  733. * @author adam
  734. * @param flags Currently ony 'noscale' used to prevent scaling of coordinates in preview mode.
  735. * @return The generated map code without the map wrapper.
  736. */
  737. imgmap.prototype.getMapInnerHTML = function(flags) {
  738. var html, coords;
  739. html = '';
  740. //foreach area properties
  741. for (var i=0, le = this.areas.length; i<le; i++) {
  742. if (this.areas[i]) {
  743. if (this.areas[i].shape && this.areas[i].shape != 'undefined') {
  744. coords = this.areas[i].lastInput;
  745. if (flags && flags.match(/noscale/)) {
  746. //for preview use real coordinates, not scaled
  747. var cs = coords.split(',');
  748. for (var j=0, le2 = cs.length; j<le2; j++) {
  749. cs[j] = Math.round(cs[j] * this.globalscale);
  750. }
  751. coords = cs.join(',');
  752. }
  753. html+= '<area shape="' + this.areas[i].shape + '"' +
  754. ' alt="' + this.areas[i].aalt + '"' +
  755. ' title="' + this.areas[i].atitle + '"' +
  756. ' coords="' + coords + '"' +
  757. ' href="' + this.areas[i].ahref + '"' +
  758. ' target="' + this.areas[i].atarget + '" />';
  759. }
  760. }
  761. }
  762. //alert(html);
  763. return html;
  764. };
  765. /**
  766. * Get the map name of the current imagemap.
  767. * If doesnt exist, nor map id, generate a new name based on timestamp.
  768. * The most portable solution is to use the same value for id and name.
  769. * This also conforms the HTML 5 specification, that says:
  770. * "If the id attribute is also specified, both attributes must have the same value."
  771. * @link http://www.w3.org/html/wg/html5/#the-map-element
  772. * @author adam
  773. * @see #getMapId
  774. * @return The name of the map.
  775. */
  776. imgmap.prototype.getMapName = function() {
  777. if (this.mapname === '') {
  778. if (this.mapid !== '') {return this.mapid;}
  779. var now = new Date();
  780. this.mapname = 'imgmap' + now.getFullYear() + (now.getMonth()+1) + now.getDate() + now.getHours() + now.getMinutes() + now.getSeconds();
  781. }
  782. return this.mapname;
  783. };
  784. /**
  785. * Get the map id of the current imagemap.
  786. * If doesnt exist, use map name.
  787. * @author adam
  788. * @see #getMapName
  789. * @return The id of the map.
  790. */
  791. imgmap.prototype.getMapId = function() {
  792. if (this.mapid === '') {
  793. this.mapid = this.getMapName();
  794. }
  795. return this.mapid;
  796. };
  797. /**
  798. * Convert wild shape names to normal ones.
  799. * @date 25-12-2008 19:27:06
  800. * @param shape The name of the shape to convert.
  801. * @return The normalized shape name, rect as default.
  802. */
  803. imgmap.prototype._normShape = function(shape) {
  804. if (!shape) {return 'rect';}
  805. shape = this.trim(shape).toLowerCase();
  806. if (shape.substring(0, 4) == 'rect') {return 'rect';}
  807. if (shape.substring(0, 4) == 'circ') {return 'circle';}
  808. if (shape.substring(0, 4) == 'poly') {return 'poly';}
  809. return 'rect';
  810. };
  811. /**
  812. * Try to normalize coordinates that came from:
  813. * 1. html textarea
  814. * 2. user input in the active area's input field
  815. * 3. from the html source in case of plugins or highlighter
  816. * Example of inputs that need to be handled:
  817. * 035,035 075,062
  818. * 150,217, 190,257, 150,297,110,257
  819. * @author adam
  820. * @param coords The coordinates in a string.
  821. * @param shape The shape of the object (rect, circle, poly, bezier1).
  822. * @param flag Flags that modify the operation. (fromcircle, frompoly, fromrect, preserve)
  823. * @returns The normalized coordinates.
  824. */
  825. imgmap.prototype._normCoords = function(coords, shape, flag) {
  826. //function level var declarations
  827. var i;//generic cycle counter
  828. var sx;//smallest x
  829. var sy;//smallest y
  830. var gx;//greatest x
  831. var gy;//greatest y
  832. var temp, le;
  833. //console.log('normcoords: ' + coords + ' - ' + shape + ' - ' + flag);
  834. coords = this.trim(coords);
  835. if (coords === '') {return '';}
  836. var oldcoords = coords;
  837. //replace some general junk
  838. coords = coords.replace(/(\d)(\D)+(\d)/g, "$1,$3");
  839. coords = coords.replace(/,\D+(\d)/g, ",$1");//cut leading junk
  840. coords = coords.replace(/,0+(\d)/g, ",$1");//cut leading zeros
  841. coords = coords.replace(/(\d)(\D)+,/g, "$1,");
  842. coords = coords.replace(/^\D+(\d)/g, "$1");//cut leading junk
  843. coords = coords.replace(/^0+(\d)/g, "$1");//cut leading zeros
  844. coords = coords.replace(/(\d)(\D)+$/g, "$1");//cut trailing junk
  845. //console.log('>'+coords + ' - ' + shape + ' - ' + flag);
  846. //now fix other issues
  847. var parts = coords.split(',');
  848. if (shape == 'rect') {
  849. if (flag == 'fromcircle') {
  850. var r = parts[2];
  851. parts[0] = parts[0] - r;
  852. parts[1] = parts[1] - r;
  853. parts[2] = parseInt(parts[0], 10) + 2 * r;
  854. parts[3] = parseInt(parts[1], 10) + 2 * r;
  855. }
  856. else if (flag == 'frompoly') {
  857. sx = parseInt(parts[0], 10); gx = parseInt(parts[0], 10);
  858. sy = parseInt(parts[1], 10); gy = parseInt(parts[1], 10);
  859. for (i=0, le = parts.length; i<le; i++) {
  860. if (i % 2 === 0 && parseInt(parts[i], 10) < sx) {
  861. sx = parseInt(parts[i], 10);}
  862. if (i % 2 === 1 && parseInt(parts[i], 10) < sy) {
  863. sy = parseInt(parts[i], 10);}
  864. if (i % 2 === 0 && parseInt(parts[i], 10) > gx) {
  865. gx = parseInt(parts[i], 10);}
  866. if (i % 2 === 1 && parseInt(parts[i], 10) > gy) {
  867. gy = parseInt(parts[i], 10);}
  868. //console.log(sx+","+sy+","+gx+","+gy);
  869. }
  870. parts[0] = sx; parts[1] = sy;
  871. parts[2] = gx; parts[3] = gy;
  872. }
  873. if (!(parseInt(parts[1], 10) >= 0)) {parts[1] = parts[0];}
  874. if (!(parseInt(parts[2], 10) >= 0)) {parts[2] = parseInt(parts[0], 10) + 10;}
  875. if (!(parseInt(parts[3], 10) >= 0)) {parts[3] = parseInt(parts[1], 10) + 10;}
  876. if (parseInt(parts[0], 10) > parseInt(parts[2], 10)) {
  877. temp = parts[0];
  878. parts[0] = parts[2];
  879. parts[2] = temp;
  880. }
  881. if (parseInt(parts[1], 10) > parseInt(parts[3], 10)) {
  882. temp = parts[1];
  883. parts[1] = parts[3];
  884. parts[3] = temp;
  885. }
  886. coords = parts[0]+","+parts[1]+","+parts[2]+","+parts[3];
  887. //console.log(coords);
  888. }
  889. else if (shape == 'circle') {
  890. if (flag == 'fromrect') {
  891. sx = parseInt(parts[0], 10); gx = parseInt(parts[2], 10);
  892. sy = parseInt(parts[1], 10); gy = parseInt(parts[3], 10);
  893. //use smaller side
  894. parts[2] = (gx - sx < gy - sy) ? gx - sx : gy - sy;
  895. parts[2] = Math.floor(parts[2] / 2);//radius
  896. parts[0] = sx + parts[2];
  897. parts[1] = sy + parts[2];
  898. }
  899. else if (flag == 'frompoly') {
  900. sx = parseInt(parts[0], 10); gx = parseInt(parts[0], 10);
  901. sy = parseInt(parts[1], 10); gy = parseInt(parts[1], 10);
  902. for (i=0, le = parts.length; i<le; i++) {
  903. if (i % 2 === 0 && parseInt(parts[i], 10) < sx) {
  904. sx = parseInt(parts[i], 10);}
  905. if (i % 2 === 1 && parseInt(parts[i], 10) < sy) {
  906. sy = parseInt(parts[i], 10);}
  907. if (i % 2 === 0 && parseInt(parts[i], 10) > gx) {
  908. gx = parseInt(parts[i], 10);}
  909. if (i % 2 === 1 && parseInt(parts[i], 10) > gy) {
  910. gy = parseInt(parts[i], 10);}
  911. //console.log(sx+","+sy+","+gx+","+gy);
  912. }
  913. //use smaller side
  914. parts[2] = (gx - sx < gy - sy) ? gx - sx : gy - sy;
  915. parts[2] = Math.floor(parts[2] / 2);//radius
  916. parts[0] = sx + parts[2];
  917. parts[1] = sy + parts[2];
  918. }
  919. if (!(parseInt(parts[1], 10) > 0)) {parts[1] = parts[0];}
  920. if (!(parseInt(parts[2], 10) > 0)) {parts[2] = 10;}
  921. coords = parts[0]+","+parts[1]+","+parts[2];
  922. }
  923. else if (shape == 'poly') {
  924. if (flag == 'fromrect') {
  925. parts[4] = parts[2];
  926. parts[5] = parts[3];
  927. parts[2] = parts[0];
  928. parts[6] = parts[4];
  929. parts[7] = parts[1];
  930. }
  931. else if (flag == 'fromcircle') {
  932. // @ url http://www.pixelwit.com/blog/2007/06/29/basic-circle-drawing-actionscript/
  933. var centerX = parseInt(parts[0], 10);
  934. var centerY = parseInt(parts[1], 10);
  935. var radius = parseInt(parts[2], 10);
  936. var j = 0;
  937. parts[j++] = centerX + radius;
  938. parts[j++] = centerY;
  939. var sides = 60;//constant = sides the fake circle will have
  940. for (i=0; i<=sides; i++) {
  941. var pointRatio = i/sides;
  942. var xSteps = Math.cos(pointRatio*2*Math.PI);
  943. var ySteps = Math.sin(pointRatio*2*Math.PI);
  944. var pointX = centerX + xSteps * radius;
  945. var pointY = centerY + ySteps * radius;
  946. parts[j++] = Math.round(pointX);
  947. parts[j++] = Math.round(pointY);
  948. }
  949. //console.log(parts);
  950. }
  951. coords = parts.join(',');
  952. }
  953. else if (shape == 'bezier1') {
  954. coords = parts.join(',');
  955. }
  956. if (flag == 'preserve' && oldcoords != coords) {
  957. //return original and throw error
  958. //throw "invalid coords";
  959. return oldcoords;
  960. }
  961. return coords;
  962. };
  963. /**
  964. * Sets the coordinates according to the given HTML map code or DOM object.
  965. * @author Adam Maschek (adam.maschek(at)gmail.com)
  966. * @date 2006-06-07 11:47:16
  967. * @param map DOM object or string of a map you want to apply.
  968. * @return True on success
  969. */
  970. imgmap.prototype.setMapHTML = function(map) {
  971. if (this.viewmode === 1) {return;}//exit if preview mode
  972. this.fireEvent('onSetMap', map);
  973. //this.log(map);
  974. //remove all areas
  975. this.removeAllAreas();
  976. //console.log(this.areas);
  977. var oMap;
  978. if (typeof map == 'string') {
  979. var oHolder = document.createElement('DIV');
  980. oHolder.innerHTML = map;
  981. oMap = oHolder.firstChild;
  982. }
  983. else if (typeof map == 'object') {
  984. oMap = map;
  985. }
  986. if (!oMap || oMap.nodeName.toLowerCase() !== 'map') {return false;}
  987. this.mapname = oMap.name;
  988. this.mapid = oMap.id;
  989. var newareas = oMap.getElementsByTagName('area');
  990. var shape, coords, href, alt, title, target, id;
  991. for (var i=0, le = newareas.length; i<le; i++) {
  992. shape = coords = href = alt = title = target = '';
  993. id = this.addNewArea();//btw id == this.currentid, just this form is a bit clearer
  994. shape = this._normShape(newareas[i].getAttribute('shape', 2));
  995. this.initArea(id, shape);
  996. if (newareas[i].getAttribute('coords', 2)) {
  997. //normalize coords
  998. coords = this._normCoords(newareas[i].getAttribute('coords', 2), shape);
  999. this.areas[id].lastInput = coords;
  1000. //for area this one will be set in recalculate
  1001. }
  1002. href = newareas[i].getAttribute('href', 2);
  1003. // FCKeditor stored url to prevent mangling from the browser.
  1004. var sSavedUrl = newareas[i].getAttribute( '_fcksavedurl' );
  1005. if (sSavedUrl) {
  1006. href = sSavedUrl;
  1007. }
  1008. if (href) {
  1009. this.areas[id].ahref = href;
  1010. }
  1011. alt = newareas[i].getAttribute('alt');
  1012. if (alt) {
  1013. this.areas[id].aalt = alt;
  1014. }
  1015. title = newareas[i].getAttribute('title');
  1016. if (!title) {title = alt;}
  1017. if (title) {
  1018. this.areas[id].atitle = title;
  1019. }
  1020. target = newareas[i].getAttribute('target');
  1021. if (target) {target = target.toLowerCase();}
  1022. // if (target == '') target = '_self';
  1023. this.areas[id].atarget = target;
  1024. this._recalculate(id, coords);//contains repaint
  1025. this.relaxArea(id);
  1026. this.fireEvent('onAreaChanged', this.areas[id]);
  1027. }//end for areas
  1028. this.fireEvent('onHtmlChanged', this.getMapHTML());
  1029. return true;
  1030. };
  1031. /**
  1032. * Preview image with imagemap applied.
  1033. * @author Adam Maschek (adam.maschek(at)gmail.com)
  1034. * @date 2006-06-06 14:51:01
  1035. * @url http://www.quirksmode.org/bugreports/archives/2005/03/Usemap_attribute_wrongly_case_sensitive.html
  1036. * @return False on error, 0 when switched to edit mode, 1 when switched to preview mode
  1037. */
  1038. imgmap.prototype.togglePreview = function() {
  1039. var i, le;//generic cycle counter
  1040. if (!this.pic) {return false;}//exit if pic is undefined
  1041. //dynamically create preview container
  1042. if (!this.preview) {
  1043. this.preview = document.createElement('DIV');
  1044. this.preview.style.display = 'none';
  1045. this.pic_container.appendChild(this.preview);
  1046. }
  1047. if (this.viewmode === 0) {
  1048. //hide canvas elements and labels
  1049. for (i = 0, le = this.areas.length; i < le; i++) {
  1050. if (this.areas[i]) {
  1051. this.areas[i].style.display = 'none';
  1052. if (this.areas[i].label) {this.areas[i].label.style.display = 'none';}
  1053. }
  1054. }
  1055. //activate image map
  1056. this.preview.innerHTML = this.getMapHTML('noscale');
  1057. this.pic.setAttribute('border', '0', 0);
  1058. this.pic.setAttribute('usemap', '#' + this.mapname, 0);
  1059. this.pic.style.cursor = 'auto';
  1060. this.viewmode = 1;
  1061. this.statusMessage(this.strings.PREVIEW_MODE);
  1062. }
  1063. else {
  1064. //show canvas elements
  1065. for (i = 0, le = this.areas.length; i < le; i++) {
  1066. if (this.areas[i]) {
  1067. this.areas[i].style.display = '';
  1068. if (this.areas[i].label && this.config.label) {this.areas[i].label.style.display = '';}
  1069. }
  1070. }
  1071. //clear image map
  1072. this.preview.innerHTML = '';
  1073. this.pic.style.cursor = this.config.cursor_default;
  1074. this.pic.removeAttribute('usemap', 0);
  1075. this.viewmode = 0;
  1076. this.statusMessage(this.strings.DESIGN_MODE);
  1077. this.is_drawing = 0;
  1078. }
  1079. this.fireEvent('onModeChanged', this.viewmode);
  1080. return this.viewmode;
  1081. };
  1082. /**
  1083. * Adds a new area. It will later become a canvas.
  1084. * GUI should use the onAddArea callback to act accordingly.
  1085. * @author Adam Maschek (adam.maschek(at)gmail.com)
  1086. * @date 2006-06-06 16:49:25
  1087. * @see #initArea
  1088. */
  1089. imgmap.prototype.addNewArea = function() {
  1090. if (this.viewmode === 1) {return;}//exit if preview mode
  1091. var lastarea = this._getLastArea();
  1092. var id = (lastarea) ? lastarea.aid + 1 : 0;
  1093. //alert(id);
  1094. //insert new possibly? unknown area (will be initialized at mousedown)
  1095. this.areas[id] = document.createElement('DIV');
  1096. this.areas[id].id = this.mapname + 'area' + id;
  1097. this.areas[id].aid = id;
  1098. this.areas[id].shape = "undefined";
  1099. this.currentid = id;
  1100. this.fireEvent('onAddArea', id);
  1101. return id;
  1102. };
  1103. /**
  1104. * Initialize a new area.
  1105. * Create the canvas, initialize it.
  1106. * Reset area parameters.
  1107. * @param id The id of the area (already existing with undefined shape)
  1108. * @param shape The shape the area will have (rect, circle, poly, bezier1)
  1109. */
  1110. imgmap.prototype.initArea = function(id, shape) {
  1111. if (!this.areas[id]) {return false;}//if all was erased, return
  1112. //remove preinited dummy div or already placed canvas
  1113. if (this.areas[id].parentNode) {this.areas[id].parentNode.removeChild(this.areas[id]);}
  1114. if (this.areas[id].label) {this.areas[id].label.parentNode.removeChild(this.areas[id].label);}
  1115. this.areas[id] = null;
  1116. //create CANVAS node
  1117. this.areas[id] = document.createElement('CANVAS');
  1118. this.pic_container.appendChild(this.areas[id]);
  1119. this.pic_container.style.position = 'relative';
  1120. //alert('init' + typeof G_vmlCanvasManager);
  1121. if (typeof G_vmlCanvasManager != "undefined") {
  1122. //override CANVAS with VML object
  1123. this.areas[id] = G_vmlCanvasManager.initElement(this.areas[id]);
  1124. //this.areas[id] = this.pic.parentNode.lastChild;
  1125. }
  1126. this.areas[id].id = this.mapname + 'area' + id;
  1127. this.areas[id].aid = id;
  1128. this.areas[id].shape = shape;
  1129. this.areas[id].ahref = '';
  1130. this.areas[id].atitle = '';
  1131. this.areas[id].aalt = '';
  1132. this.areas[id].atarget = ''; // '_self';
  1133. this.areas[id].style.position = 'absolute';
  1134. this.areas[id].style.top = this.pic.offsetTop + 'px';
  1135. this.areas[id].style.left = this.pic.offsetLeft + 'px';
  1136. this._setopacity(this.areas[id], this.config.CL_DRAW_BG, this.config.draw_opacity);
  1137. //hook event handlers
  1138. this.areas[id].ondblclick = this.area_dblclick.bind(this);
  1139. this.areas[id].onmousedown = this.area_mousedown.bind(this);
  1140. this.areas[id].onmouseup = this.area_mouseup.bind(this);
  1141. this.areas[id].onmousemove = this.area_mousemove.bind(this);
  1142. this.areas[id].onmouseover = this.area_mouseover.bind(this);
  1143. this.areas[id].onmouseout = this.area_mouseout.bind(this);
  1144. //initialize memory object
  1145. this.memory[id] = {};
  1146. this.memory[id].downx = 0;
  1147. this.memory[id].downy = 0;
  1148. this.memory[id].left = 0;
  1149. this.memory[id].top = 0;
  1150. this.memory[id].width = 0;
  1151. this.memory[id].height = 0;
  1152. this.memory[id].xpoints = [];
  1153. this.memory[id].ypoints = [];
  1154. //create label node
  1155. this.areas[id].label = document.createElement('DIV');
  1156. this.pic_container.appendChild(this.areas[id].label);
  1157. this.areas[id].label.className = this.config.label_class;
  1158. this.assignCSS(this.areas[id].label, this.config.label_style);
  1159. this.areas[id].label.style.position = 'absolute';
  1160. };
  1161. /**
  1162. * Resets area border and opacity to a normal state after drawing.
  1163. * @author Adam Maschek (adam.maschek(at)gmail.com)
  1164. * @date 15-02-2007 22:07:28
  1165. * @param id The id of the area.
  1166. * @see #relaxAllAreas
  1167. */
  1168. imgmap.prototype.relaxArea = function(id) {
  1169. if (!this.areas[id]) {return;}
  1170. this.fireEvent('onRelaxArea', id);
  1171. this._setBorder(id, 'NORM');
  1172. this._setopacity(this.areas[id], this.config.CL_NORM_BG, this.config.norm_opacity);
  1173. };
  1174. /**
  1175. * Resets area border and opacity of all areas.
  1176. * Calls relaxArea on each of them.
  1177. * @author Adam Maschek (adam.maschek(at)gmail.com)
  1178. * @date 23-04-2007 23:31:09
  1179. * @see #relaxArea
  1180. */
  1181. imgmap.prototype.relaxAllAreas = function() {
  1182. for (var i=0, le = this.areas.length; i<le; i++) {
  1183. if (this.areas[i]) {
  1184. this.relaxArea(i);
  1185. }
  1186. }
  1187. };
  1188. /**
  1189. * Set border of a given area according to style flag.
  1190. * Possible values of style: NORM, HIGHLIGHT, DRAW.
  1191. * Non-rectangle shapes wont get a border if config.bounding_box is false.
  1192. * @date 26-12-2008 22:34:41
  1193. * @param id The id of the area to set the border on.
  1194. * @param style Coloring style (NORM, HIGHLIGHT, DRAW), see relevant colors in config.
  1195. * @since 2.1
  1196. */
  1197. imgmap.prototype._setBorder = function(id, style) {
  1198. if (this.areas[id].shape == 'rect' || this.config.bounding_box) {
  1199. this.areas[id].style.borderWidth = '1px';
  1200. this.areas[id].style.borderStyle = (style == 'DRAW' ? 'dotted' : 'solid');
  1201. this.areas[id].style.borderColor = this.config['CL_' + style + '_' + (this.areas[id].shape == 'rect' ? 'SHAPE' : 'BOX')];
  1202. }
  1203. else {
  1204. //clear border
  1205. this.areas[id].style.border = '';
  1206. }
  1207. };
  1208. /**
  1209. * Set opacity of area to the given percentage, as well as set the background color.
  1210. * If percentage contains a dash(-), the setting of the opacity will be gradual.
  1211. * @param area The area object.
  1212. * @param bgcolor New background color
  1213. * @param pct Percentage of the opacity.
  1214. */
  1215. imgmap.prototype._setopacity = function(area, bgcolor, pct) {
  1216. if (bgcolor) {area.style.backgroundColor = bgcolor;}
  1217. if (pct && typeof pct == 'string' && pct.match(/^\d*\-\d+$/)) {
  1218. //gradual fade
  1219. var parts = pct.split('-');
  1220. if (typeof parts[0] != 'undefined') {
  1221. //set initial opacity
  1222. parts[0] = parseInt(parts[0], 10);
  1223. this._setopacity(area, bgcolor, parts[0]);
  1224. }
  1225. if (typeof parts[1] != 'undefined') {
  1226. parts[1] = parseInt(parts[1], 10);
  1227. var curr = this._getopacity(area);
  1228. //this.log('curr: '+curr);
  1229. var _this = this;
  1230. var diff = Math.round(parts[1] - curr);
  1231. if (diff > 5) {
  1232. window.setTimeout(function () {_this._setopacity(area, null, '-'+parts[1]);}, 20);
  1233. pct = 1*curr + 5;
  1234. }
  1235. else if (diff < -3) {
  1236. window.setTimeout(function () {_this._setopacity(area, null, '-'+parts[1]);}, 20);
  1237. pct = 1*curr - 3;
  1238. }
  1239. else {
  1240. //final set
  1241. pct = parts[1];
  1242. }
  1243. }
  1244. }
  1245. if (!isNaN(pct)) {
  1246. pct = Math.round(parseInt(pct, 10));
  1247. //this.log('set ('+area.aid+'): ' + pct, 1);
  1248. area.style.opacity = pct / 100;
  1249. area.style.filter = 'alpha(opacity='+pct+')';
  1250. }
  1251. };
  1252. /**
  1253. * Get the currently set opacity of a given area.
  1254. * @author adam
  1255. * @param area The area (canvas) you want to get opacity info from.
  1256. * @return Opacity value in a range of 0-100.
  1257. */
  1258. imgmap.prototype._getopacity = function(area) {
  1259. if (area.style.opacity <= 1) {
  1260. return area.style.opacity * 100;
  1261. }
  1262. if (area.style.filter) {
  1263. //alpha(opacity=NaN)
  1264. return parseInt(area.style.filter.replace(/alpha\(opacity\=([^\)]*)\)/ig, "$1"), 10);
  1265. }
  1266. return 100;//default opacity
  1267. };
  1268. /**
  1269. * Removes the area marked by id.
  1270. * removeAllAreas will indicate a mass flag so that the output HTML will only be updated at
  1271. * the end of the operation.
  1272. * Callback will call the GUI code to remove GUI elements.
  1273. * @author Adam Maschek (adam.maschek(at)gmail.com)
  1274. * @date 11-02-2007 20:40:58
  1275. * @param id The id of the area to remove.
  1276. * @param mass Flag to indicate skipping the call of onHtmlChanged callback
  1277. * @see #removeAllAreas
  1278. */
  1279. imgmap.prototype.removeArea = function(id, mass) {
  1280. if (this.viewmode === 1) {return;}//exit if preview mode
  1281. if (id === null || typeof id == "undefined") {return;}//exit if no id given
  1282. try {
  1283. //remove area and label
  1284. //explicitly set some values to null to avoid IE circular reference memleak
  1285. this.areas[id].label.parentNode.removeChild(this.areas[id].label);
  1286. this.areas[id].parentNode.removeChild(this.areas[id]);
  1287. this.areas[id].label.className = null;
  1288. //this.areas[id].label.style = null;
  1289. //console.log(this.areas[id].label);
  1290. this.areas[id].label = null;
  1291. this.areas[id].onmouseover = null;
  1292. this.areas[id].onmouseout = null;
  1293. this.areas[id].onmouseup = null;
  1294. this.areas[id].onmousedown = null;
  1295. this.areas[id].onmousemove = null;
  1296. // console.log(this.areas[id].label);
  1297. }
  1298. catch (err) {
  1299. //alert('noparent');
  1300. }
  1301. this.areas[id] = null;
  1302. this.fireEvent('onRemoveArea', id);
  1303. //update grand html
  1304. if (!mass) {this.fireEvent('onHtmlChanged', this.getMapHTML());}
  1305. };
  1306. /**
  1307. * Removes all areas.
  1308. * Will call removeArea on all areas.
  1309. * @author Adam Maschek (adam.maschek(at)gmail.com)
  1310. * @date 2006-06-07 11:55:34
  1311. * @see #removeArea
  1312. */
  1313. imgmap.prototype.removeAllAreas = function() {
  1314. for (var i = 0, le = this.areas.length; i < le; i++) {
  1315. if (this.areas[i]) {
  1316. this.removeArea(i, true);
  1317. }
  1318. }
  1319. //only call this at the end, use mass param above to avoid calling it in process
  1320. this.fireEvent('onHtmlChanged', this.getMapHTML());
  1321. };
  1322. /**
  1323. * Scales all areas.
  1324. * Will store scale parameter in globalscale property.
  1325. * This is needed to know how to draw new areas on an already scaled canvas.
  1326. * @author adam
  1327. * @date 02-11-2008 14:13:14
  1328. * @param scale Scale factor (1-original, 0.5-half, 2-double, etc.)
  1329. */
  1330. imgmap.prototype.scaleAllAreas = function(scale) {
  1331. var rscale = 1;//relative scale
  1332. try {
  1333. rscale = scale / this.globalscale;
  1334. }
  1335. catch (err) {
  1336. this.log("Invalid (global)scale", 1);
  1337. }
  1338. //console.log('gscale: '+this.globalscale);
  1339. //console.log('scale: '+scale);
  1340. //console.log('rscale: '+rscale);
  1341. this.globalscale = scale;
  1342. for (var i = 0, le = this.areas.length; i < le; i++) {
  1343. if (this.areas[i] && this.areas[i].shape != 'undefined') {
  1344. this.scaleArea(i, rscale);
  1345. }
  1346. }
  1347. };
  1348. /**
  1349. * Scales one area.
  1350. * @author adam
  1351. * @date 02-11-2008 14:13:14
  1352. * @param rscale Relative scale factor (1-keep, 0.5-half, 2-double, etc.)
  1353. */
  1354. imgmap.prototype.scaleArea = function(id, rscale) {
  1355. //set position and new dimensions
  1356. this.areas[id].style.top = parseInt(this.areas[id].style.top, 10) * rscale + 'px';
  1357. this.areas[id].style.left = parseInt(this.areas[id].style.left, 10) * rscale + 'px';
  1358. this.setAreaSize(id, this.areas[id].width * rscale, this.areas[id].height * rscale);
  1359. //handle polygon/bezier coordinates scaling
  1360. if (this.areas[id].shape == 'poly' || this.areas[id].shape == 'bezier1') {
  1361. for (var i=0, le = this.areas[id].xpoints.length; i<le; i++) {
  1362. this.areas[id].xpoints[i]*= rscale;
  1363. this.areas[id].ypoints[i]*= rscale;
  1364. }
  1365. }
  1366. this._repaint(this.areas[id], this.config.CL_NORM_SHAPE);
  1367. this._updatecoords(id);
  1368. };
  1369. /**
  1370. * Put label in the top left corner according to label config.
  1371. * By default it will contain the number of the area (area.aid)
  1372. * @param id The id of the area to add label to.
  1373. */
  1374. imgmap.prototype._putlabel = function(id) {
  1375. if (this.viewmode === 1) {return;}//exit if preview mode
  1376. if (!this.areas[id].label) {return;}//not yet inited
  1377. try {
  1378. if (!this.config.label) {
  1379. this.areas[id].label.innerHTML = '';
  1380. this.areas[id].label.style.display = 'none';
  1381. }
  1382. else {
  1383. this.areas[id].label.style.display = '';
  1384. var label = this.config.label;
  1385. label = label.replace(/%n/g, String(id));
  1386. label = label.replace(/%c/g, String(this.areas[id].lastInput));
  1387. label = label.replace(/%h/g, String(this.areas[id].ahref));
  1388. label = label.replace(/%a/g, String(this.areas[id].aalt));
  1389. label = label.replace(/%t/g, String(this.areas[id].atitle));
  1390. this.areas[id].label.innerHTML = label;
  1391. }
  1392. //align to the top left corner
  1393. this.areas[id].label.style.top = this.areas[id].style.top;
  1394. this.areas[id].label.style.left = this.areas[id].style.left;
  1395. }
  1396. catch (err) {
  1397. this.log("Error putting label", 1);
  1398. }
  1399. };
  1400. /**
  1401. * Set area title and alt (for IE) according to the hint configuration.
  1402. * This will show up in the usual yellow box when you hover over with the mouse.
  1403. * @param id The id of the area to set hint at.
  1404. */
  1405. imgmap.prototype._puthint = function(id) {
  1406. try {
  1407. if (!this.config.hint) {
  1408. this.areas[id].title = '';
  1409. this.areas[id].alt = '';
  1410. }
  1411. else {
  1412. var hint = this.config.hint;
  1413. hint = hint.replace(/%n/g, String(id));
  1414. hint = hint.replace(/%c/g, String(this.areas[id].lastInput));
  1415. hint = hint.replace(/%h/g, String(this.areas[id].ahref));
  1416. hint = hint.replace(/%a/g, String(this.areas[id].aalt));
  1417. hint = hint.replace(/%t/g, String(this.areas[id].atitle));
  1418. this.areas[id].title = hint;
  1419. this.areas[id].alt = hint;
  1420. }
  1421. }
  1422. catch (err) {
  1423. this.log("Error putting hint", 1);
  1424. }
  1425. };
  1426. /**
  1427. * Will call repaint on all areas.
  1428. * Useful when you change labeling or hint config on the GUI.
  1429. * @see #_repaint
  1430. */
  1431. imgmap.prototype._repaintAll = function() {
  1432. for (var i=0, le = this.areas.length; i<le; i++) {
  1433. if (this.areas[i]) {
  1434. this._repaint(this.areas[i], this.config.CL_NORM_SHAPE);
  1435. }
  1436. }
  1437. };
  1438. /**
  1439. * Repaints the actual canvas content.
  1440. * This is the only canvas drawing magic that is happening.
  1441. * In fact rectangles will not have any canvas content, just a normal css border.
  1442. * After repainting the canvas, it will call putlabel and puthint methods.
  1443. * @param area The area object.
  1444. * @param color Color of the line to draw on the canvas.
  1445. * @param x Only used for polygons/beziers as the newest control point x.
  1446. * @param y Only used for polygons/beziers as the newest control point y.
  1447. */
  1448. imgmap.prototype._repaint = function(area, color, x, y) {
  1449. var ctx;//canvas context
  1450. var width, height, left, top;//canvas properties
  1451. var i, le;//loop counter
  1452. if (area.shape == 'circle') {
  1453. width = parseInt(area.style.width, 10);
  1454. var radius = Math.floor(width/2) - 1;
  1455. if (radius<0)
  1456. radius=0;
  1457. //get canvas context
  1458. //alert(area.tagName);
  1459. ctx = area.getContext("2d");
  1460. //clear canvas
  1461. ctx.clearRect(0, 0, width, width);
  1462. //draw circle
  1463. ctx.beginPath();
  1464. ctx.strokeStyle = color;
  1465. ctx.arc(radius, radius, radius, 0, Math.PI*2, 0);
  1466. ctx.stroke();
  1467. ctx.closePath();
  1468. //draw center
  1469. ctx.strokeStyle = this.config.CL_KNOB;
  1470. ctx.strokeRect(radius, radius, 1, 1);
  1471. //put label
  1472. this._putlabel(area.aid);
  1473. this._puthint(area.aid);
  1474. }
  1475. else if (area.shape == 'rect') {
  1476. //put label
  1477. this._putlabel(area.aid);
  1478. this._puthint(area.aid);
  1479. }
  1480. else if (area.shape == 'poly') {
  1481. width = parseInt(area.style.width, 10);
  1482. height = parseInt(area.style.height, 10);
  1483. left = parseInt(area.style.left, 10);
  1484. top = parseInt(area.style.top, 10);
  1485. if (area.xpoints) {
  1486. //get canvas context
  1487. ctx = area.getContext("2d");
  1488. //clear canvas
  1489. ctx.clearRect(0, 0, width, height);
  1490. //draw polygon
  1491. ctx.beginPath();
  1492. ctx.strokeStyle = color;
  1493. ctx.moveTo(area.xpoints[0] - left, area.ypoints[0] - top);
  1494. for (i = 1, le = area.xpoints.length; i < le; i++) {
  1495. ctx.lineTo(area.xpoints[i] - left , area.ypoints[i] - top);
  1496. }
  1497. if (this.is_drawing == this.DM_POLYGON_DRAW || this.is_drawing == this.DM_POLYGON_LASTDRAW) {
  1498. //only draw to the current position if not moving
  1499. ctx.lineTo(x - left - 5 , y - top - 5);
  1500. }
  1501. ctx.lineTo(area.xpoints[0] - left , area.ypoints[0] - top);
  1502. ctx.stroke();
  1503. ctx.closePath();
  1504. }
  1505. //put label
  1506. this._putlabel(area.aid);
  1507. this._puthint(area.aid);
  1508. }
  1509. else if (area.shape == 'bezier1') {
  1510. width = parseInt(area.style.width, 10);
  1511. height = parseInt(area.style.height, 10);
  1512. left = parseInt(area.style.left, 10);
  1513. top = parseInt(area.style.top, 10);
  1514. if (area.xpoints) {
  1515. //get canvas context
  1516. ctx = area.getContext("2d");
  1517. //clear canvas
  1518. ctx.clearRect(0, 0, width, height);
  1519. //draw bezier1 (every second point is control point)
  1520. ctx.beginPath();
  1521. ctx.strokeStyle = color;
  1522. //move to the beginning position
  1523. ctx.moveTo(area.xpoints[0] - left, area.ypoints[0] - top);
  1524. //draw previous points - use every second point only
  1525. for (i = 2, le = area.xpoints.length; i < le; i+= 2) {
  1526. ctx.quadraticCurveTo(area.xpoints[i-1] - left, area.ypoints[i-1] - top, area.xpoints[i] - left, area.ypoints[i] - top);
  1527. }
  1528. if (this.is_drawing == this.DM_BEZIER_DRAW || this.is_drawing == this.DM_BEZIER_LASTDRAW) {
  1529. //only draw to the current position if not moving
  1530. if (area.xpoints.length % 2 === 0 && area.xpoints.length > 1) {
  1531. //drawing point - draw a curve to it using the previous control point
  1532. ctx.quadraticCurveTo(area.xpoints[area.xpoints.length - 1] - left - 5 , area.ypoints[area.ypoints.length - 1] - top - 5, x - left - 5 , y - top - 5);
  1533. }
  1534. else {
  1535. //control point - simply draw a line to it
  1536. ctx.lineTo(x - left - 5 , y - top - 5);
  1537. }
  1538. }
  1539. //close area by drawing a line to the first point
  1540. ctx.lineTo(area.xpoints[0] - left , area.ypoints[0] - top);
  1541. ctx.stroke();
  1542. ctx.closePath();
  1543. }
  1544. //put label
  1545. this._putlabel(area.aid);
  1546. this._puthint(area.aid);
  1547. }
  1548. };
  1549. /**
  1550. * Updates Area coordinates.
  1551. * Called when needed, eg. on mousemove, mousedown.
  1552. * Also updates html container value (thru hook).
  1553. * Calls callback onAreaChanged and onHtmlChanged so that GUI can follow.
  1554. * This is an important hook to your GUI.
  1555. * Uses globalscale to scale real coordinates to area coordinates.
  1556. * @date 2006.10.24. 22:39:27
  1557. * @author Adam Maschek (adam.maschek(at)gmail.com)
  1558. * @param id The id of the area.
  1559. */
  1560. imgmap.prototype._updatecoords = function(id) {
  1561. var left = Math.round(parseInt(this.areas[id].style.left, 10) / this.globalscale);
  1562. var top = Math.round(parseInt(this.areas[id].style.top, 10) / this.globalscale);
  1563. var height = Math.round(parseInt(this.areas[id].style.height, 10) / this.globalscale);
  1564. var width = Math.round(parseInt(this.areas[id].style.width, 10) / this.globalscale);
  1565. var value = '';
  1566. if (this.areas[id].shape == 'rect') {
  1567. value = left + ',' + top + ',' + (left + width) + ',' + (top + height);
  1568. this.areas[id].lastInput = value;
  1569. }
  1570. else if (this.areas[id].shape == 'circle') {
  1571. var radius = Math.floor(width/2) - 1;
  1572. value = (left + radius) + ',' + (top + radius) + ',' + radius;
  1573. this.areas[id].lastInput = value;
  1574. }
  1575. else if (this.areas[id].shape == 'poly' || this.areas[id].shape == 'bezier1') {
  1576. if (this.areas[id].xpoints) {
  1577. for (var i=0, le = this.areas[id].xpoints.length; i<le; i++) {
  1578. value+= Math.round(this.areas[id].xpoints[i] / this.globalscale) + ',' +
  1579. Math.round(this.areas[id].ypoints[i] / this.globalscale) + ',';
  1580. }
  1581. value = value.substring(0, value.length - 1);
  1582. }
  1583. this.areas[id].lastInput = value;
  1584. }
  1585. this.fireEvent('onAreaChanged', this.areas[id]);
  1586. this.fireEvent('onHtmlChanged', this.getMapHTML());
  1587. };
  1588. /**
  1589. * Updates the visual representation of the area with the given id according
  1590. * to the new coordinates that typically come from an input on the GUI.
  1591. * Uses globalscale to scale area coordinates to real coordinates.
  1592. * @date 2006.10.24. 22:46:55
  1593. * @author Adam Maschek (adam.maschek(at)gmail.com)
  1594. * @param id The id of the area.
  1595. * @param coords The new coords, they will be normalized.
  1596. */
  1597. imgmap.prototype._recalculate = function(id, coords) {
  1598. try {
  1599. if (coords) {
  1600. coords = this._normCoords(coords, this.areas[id].shape, 'preserve');
  1601. }
  1602. else {
  1603. coords = this.areas[id].lastInput || '' ;
  1604. }
  1605. var parts = coords.split(',');
  1606. if (this.areas[id].shape == 'rect') {
  1607. if (parts.length != 4 ||
  1608. parseInt(parts[0], 10) > parseInt(parts[2], 10) ||
  1609. parseInt(parts[1], 10) > parseInt(parts[3], 10)) {throw "invalid coords";}
  1610. this.areas[id].style.left = this.globalscale * (this.pic.offsetLeft + parseInt(parts[0], 10)) + 'px';
  1611. this.areas[id].style.top = this.globalscale * (this.pic.offsetTop + parseInt(parts[1], 10)) + 'px';
  1612. this.setAreaSize(id, this.globalscale * (parts[2] - parts[0]), this.globalscale * (parts[3] - parts[1]));
  1613. this._repaint(this.areas[id], this.config.CL_NORM_SHAPE);
  1614. }
  1615. else if (this.areas[id].shape == 'circle') {
  1616. if (parts.length != 3 ||
  1617. parseInt(parts[2], 10) < 0) {throw "invalid coords";}
  1618. var width = 2 * (parts[2]);
  1619. //alert(parts[2]);
  1620. //alert(width);
  1621. this.setAreaSize(id, this.globalscale * width, this.globalscale * width);
  1622. this.areas[id].style.left = this.globalscale * (this.pic.offsetLeft + parseInt(parts[0], 10) - width/2) + 'px';
  1623. this.areas[id].style.top = this.globalscale * (this.pic.offsetTop + parseInt(parts[1], 10) - width/2) + 'px';
  1624. this._repaint(this.areas[id], this.config.CL_NORM_SHAPE);
  1625. }
  1626. else if (this.areas[id].shape == 'poly' || this.areas[id].shape == 'bezier1') {
  1627. if (parts.length < 2) {throw "invalid coords";}
  1628. this.areas[id].xpoints = [];
  1629. this.areas[id].ypoints = [];
  1630. for (var i=0, le = parts.length; i<le; i+=2) {
  1631. this.areas[id].xpoints[this.areas[id].xpoints.length] = this.globalscale * (this.pic.offsetLeft + parseInt(parts[i], 10));
  1632. this.areas[id].ypoints[this.areas[id].ypoints.length] = this.globalscale * (this.pic.offsetTop + parseInt(parts[i+1], 10));
  1633. this._polygongrow(this.areas[id], this.globalscale * parts[i], this.globalscale * parts[i+1]);
  1634. }
  1635. this._polygonshrink(this.areas[id]);//includes repaint
  1636. }
  1637. }
  1638. catch (err) {
  1639. var msg = (err.message) ? err.message : 'error calculating coordinates';
  1640. this.log(msg, 1);
  1641. this.statusMessage(this.strings.ERR_INVALID_COORDS);
  1642. if (this.areas[id].lastInput) {
  1643. this.fireEvent('onAreaChanged', this.areas[id]);
  1644. }
  1645. this._repaint(this.areas[id], this.config.CL_NORM_SHAPE);
  1646. return;
  1647. }
  1648. //on success update lastInput
  1649. this.areas[id].lastInput = coords;
  1650. };
  1651. /**
  1652. * Grow polygon area to be able to contain the given new coordinates.
  1653. * @author adam
  1654. * @param area The area to grow.
  1655. * @param newx The new coordinate x.
  1656. * @param newy The new coordinate y.
  1657. * @see #_polygonshrink
  1658. */
  1659. imgmap.prototype._polygongrow = function(area, newx, newy) {
  1660. //this.log('pgrow');
  1661. var xdiff = newx - parseInt(area.style.left, 10);
  1662. var ydiff = newy - parseInt(area.style.top , 10);
  1663. var pad = 0;//padding on the edges
  1664. var pad2 = 0;//twice the padding
  1665. if (newx < parseInt(area.style.left, 10)) {
  1666. area.style.left = (newx - pad) + 'px';
  1667. this.setAreaSize(area.aid, parseInt(area.style.width, 10) + Math.abs(xdiff) + pad2, null);
  1668. }
  1669. else if (newx > parseInt(area.style.left, 10) + parseInt(area.style.width, 10)) {
  1670. this.setAreaSize(area.aid, newx - parseInt(area.style.left, 10) + pad2, null);
  1671. }
  1672. if (newy < parseInt(area.style.top, 10)) {
  1673. area.style.top = (newy - pad) + 'px';
  1674. this.setAreaSize(area.aid, null, parseInt(area.style.height, 10) + Math.abs(ydiff) + pad2);
  1675. }
  1676. else if (newy > parseInt(area.style.top, 10) + parseInt(area.style.height, 10)) {
  1677. this.setAreaSize(area.aid, null, newy - parseInt(area.style.top, 10) + pad2);
  1678. }
  1679. };
  1680. /**
  1681. * Shrink the polygon bounding area to the necessary size, by first reducing it
  1682. * to the minimum, and then gradually growing it.
  1683. * We need this because while we were drawing the polygon, it might have expanded
  1684. * the canvas more than needed.
  1685. * Will repaint the area.
  1686. * @author adam
  1687. * @param area The area to shrink.
  1688. * @see #_polygongrow
  1689. */
  1690. imgmap.prototype._polygonshrink = function(area) {
  1691. //this.log('pshrink');
  1692. area.style.left = (area.xpoints[0]) + 'px';
  1693. area.style.top = (area.ypoints[0]) + 'px';
  1694. this.setAreaSize(area.aid, 0, 0);
  1695. for (var i=0, le = area.xpoints.length; i<le; i++) {
  1696. this._polygongrow(area, area.xpoints[i], area.ypoints[i]);
  1697. }
  1698. this._repaint(area, this.config.CL_NORM_SHAPE);
  1699. };
  1700. /**
  1701. * EVENT HANDLER: Handles mousemove on the image.
  1702. * This is the main drawing routine.
  1703. * Depending on the current shape, will draw the rect/circle/poly to the new position.
  1704. * @param e The event object.
  1705. */
  1706. imgmap.prototype.img_mousemove = function(e) {
  1707. //function level var declarations
  1708. var x;
  1709. var y;
  1710. var xdiff;
  1711. var ydiff;
  1712. var diff;
  1713. if (this.viewmode === 1) {return;}//exit if preview mode
  1714. //event.x is relative to parent element, but page.x is NOT
  1715. //pos coordinates are the same absolute coords, offset coords are relative to parent
  1716. var pos = this._getPos(this.pic);
  1717. // 3.3.3 fix
  1718. x = (this.isMSIE) ? (window.event.x - this.pic.offsetLeft) : (e.clientX - pos.x);
  1719. y = (this.isMSIE) ? (window.event.y - this.pic.offsetTop) : (e.clientY - pos.y);
  1720. x = x + this.pic_container.scrollLeft;
  1721. y = y + this.pic_container.scrollTop;
  1722. //exit if outside image
  1723. if (x<0 || y<0 || x>this.pic.width || y>this.pic.height) {return;}
  1724. //old dimensions that need to be updated in this function
  1725. if (this.memory[this.currentid]) {
  1726. var top = this.memory[this.currentid].top;
  1727. var left = this.memory[this.currentid].left;
  1728. var height = this.memory[this.currentid].height;
  1729. var width = this.memory[this.currentid].width;
  1730. }
  1731. // Handle shift state for Safari
  1732. // Safari doesn't generate keyboard events for modifiers: http://bugs.webkit.org/show_bug.cgi?id=11696
  1733. if (this.isSafari) {
  1734. if (e.shiftKey) {
  1735. if (this.is_drawing == this.DM_RECTANGLE_DRAW) {
  1736. this.is_drawing = this.DM_SQUARE_DRAW;
  1737. this.statusMessage(this.strings.SQUARE2_DRAW);
  1738. }
  1739. }
  1740. else {
  1741. if (this.is_drawing == this.DM_SQUARE_DRAW && this.areas[this.currentid].shape == 'rect') {
  1742. //not for circle!
  1743. this.is_drawing = this.DM_RECTANGLE_DRAW;
  1744. this.statusMessage(this.strings.RECTANGLE_DRAW);
  1745. }
  1746. }
  1747. }
  1748. if (this.is_drawing == this.DM_RECTANGLE_DRAW) {
  1749. //rectangle mode
  1750. this.fireEvent('onDrawArea', this.currentid);
  1751. xdiff = x - this.memory[this.currentid].downx;
  1752. ydiff = y - this.memory[this.currentid].downy;
  1753. //alert(xdiff);
  1754. this.setAreaSize(this.currentid, Math.abs(xdiff), Math.abs(ydiff));
  1755. if (xdiff < 0) {
  1756. this.areas[this.currentid].style.left = (x + 1) + 'px';
  1757. }
  1758. if (ydiff < 0) {
  1759. this.areas[this.currentid].style.top = (y + 1) + 'px';
  1760. }
  1761. }
  1762. else if (this.is_drawing == this.DM_SQUARE_DRAW) {
  1763. //square mode - align to shorter side
  1764. this.fireEvent('onDrawArea', this.currentid);
  1765. xdiff = x - this.memory[this.currentid].downx;
  1766. ydiff = y - this.memory[this.currentid].downy;
  1767. if (Math.abs(xdiff) < Math.abs(ydiff)) {
  1768. diff = Math.abs(parseInt(xdiff, 10));
  1769. }
  1770. else {
  1771. diff = Math.abs(parseInt(ydiff, 10));
  1772. }
  1773. //alert(xdiff);
  1774. this.setAreaSize(this.currentid, diff, diff);
  1775. if (xdiff < 0) {
  1776. this.areas[this.currentid].style.left = (this.memory[this.currentid].downx + diff*-1) + 'px';
  1777. }
  1778. if (ydiff < 0) {
  1779. this.areas[this.currentid].style.top = (this.memory[this.currentid].downy + diff*-1 + 1) + 'px';
  1780. }
  1781. }
  1782. else if (this.is_drawing == this.DM_POLYGON_DRAW || this.is_drawing == this.DM_BEZIER_DRAW) {
  1783. //polygon or bezier mode
  1784. this.fireEvent('onDrawArea', this.currentid);
  1785. this._polygongrow(this.areas[this.currentid], x, y);
  1786. }
  1787. else if (this.is_drawing == this.DM_RECTANGLE_MOVE || this.is_drawing == this.DM_SQUARE_MOVE) {
  1788. this.fireEvent('onMoveArea', this.currentid);
  1789. x = x - this.memory[this.currentid].rdownx;
  1790. y = y - this.memory[this.currentid].rdowny;
  1791. if (x + width > this.pic.width || y + height > this.pic.height) {return;}
  1792. if (x < 0 || y < 0) {return;}
  1793. //this.log(x + ' - '+width+ '+'+this.memory[this.currentid].rdownx +'='+xdiff );
  1794. this.areas[this.currentid].style.left = x + 1 + 'px';
  1795. this.areas[this.currentid].style.top = y + 1 + 'px';
  1796. }
  1797. else if (this.is_drawing == this.DM_POLYGON_MOVE || this.is_drawing == this.DM_BEZIER_MOVE) {
  1798. this.fireEvent('onMoveArea', this.currentid);
  1799. x = x - this.memory[this.currentid].rdownx;
  1800. y = y - this.memory[this.currentid].rdowny;
  1801. if (x + width > this.pic.width || y + height > this.pic.height) {return;}
  1802. if (x < 0 || y < 0) {return;}
  1803. xdiff = x - left;
  1804. ydiff = y - top;
  1805. if (this.areas[this.currentid].xpoints) {
  1806. for (var i=0, le = this.areas[this.currentid].xpoints.length; i<le; i++) {
  1807. this.areas[this.currentid].xpoints[i] = this.memory[this.currentid].xpoints[i] + xdiff;
  1808. this.areas[this.currentid].ypoints[i] = this.memory[this.currentid].ypoints[i] + ydiff;
  1809. }
  1810. }
  1811. this.areas[this.currentid].style.left = x + 'px';
  1812. this.areas[this.currentid].style.top = y + 'px';
  1813. }
  1814. else if (this.is_drawing == this.DM_SQUARE_RESIZE_LEFT) {
  1815. this.fireEvent('onResizeArea', this.currentid);
  1816. diff = x - left;
  1817. //alert(diff);
  1818. if ((width + (-1 * diff)) > 0) {
  1819. //real resize left
  1820. this.areas[this.currentid].style.left = x + 1 + 'px';
  1821. this.areas[this.currentid].style.top = (top + (diff/2)) + 'px';
  1822. this.setAreaSize(this.currentid, parseInt(width + (-1 * diff), 10), parseInt(height + (-1 * diff), 10));
  1823. }
  1824. else {
  1825. //jump to another state
  1826. this.memory[this.currentid].width = 0;
  1827. this.memory[this.currentid].height = 0;
  1828. this.memory[this.currentid].left = x;
  1829. this.memory[this.currentid].top = y;
  1830. this.is_drawing = this.DM_SQUARE_RESIZE_RIGHT;
  1831. }
  1832. }
  1833. else if (this.is_drawing == this.DM_SQUARE_RESIZE_RIGHT) {
  1834. this.fireEvent('onResizeArea', this.currentid);
  1835. diff = x - left - width;
  1836. if ((width + (diff)) - 1 > 0) {
  1837. //real resize right
  1838. this.areas[this.currentid].style.top = (top + (-1* diff/2)) + 'px';
  1839. this.setAreaSize(this.currentid, (width + (diff)) - 1, (height + (diff)));
  1840. }
  1841. else {
  1842. //jump to another state
  1843. this.memory[this.currentid].width = 0;
  1844. this.memory[this.currentid].height = 0;
  1845. this.memory[this.currentid].left = x;
  1846. this.memory[this.currentid].top = y;
  1847. this.is_drawing = this.DM_SQUARE_RESIZE_LEFT;
  1848. }
  1849. }
  1850. else if (this.is_drawing == this.DM_SQUARE_RESIZE_TOP) {
  1851. this.fireEvent('onResizeArea', this.currentid);
  1852. diff = y - top;
  1853. if ((width + (-1 * diff)) > 0) {
  1854. //real resize top
  1855. this.areas[this.currentid].style.top = y + 1 + 'px';
  1856. this.areas[this.currentid].style.left = (left + (diff/2)) + 'px';
  1857. this.setAreaSize(this.currentid, (width + (-1 * diff)), (height + (-1 * diff)));
  1858. }
  1859. else {
  1860. //jump to another state
  1861. this.memory[this.currentid].width = 0;
  1862. this.memory[this.currentid].height = 0;
  1863. this.memory[this.currentid].left = x;
  1864. this.memory[this.currentid].top = y;
  1865. this.is_drawing = this.DM_SQUARE_RESIZE_BOTTOM;
  1866. }
  1867. }
  1868. else if (this.is_drawing == this.DM_SQUARE_RESIZE_BOTTOM) {
  1869. this.fireEvent('onResizeArea', this.currentid);
  1870. diff = y - top - height;
  1871. if ((width + (diff)) - 1 > 0) {
  1872. //real resize bottom
  1873. this.areas[this.currentid].style.left = (left + (-1* diff/2)) + 'px';
  1874. this.setAreaSize(this.currentid, (width + (diff)) - 1 , (height + (diff)) - 1);
  1875. }
  1876. else {
  1877. //jump to another state
  1878. this.memory[this.currentid].width = 0;
  1879. this.memory[this.currentid].height = 0;
  1880. this.memory[this.currentid].left = x;
  1881. this.memory[this.currentid].top = y;
  1882. this.is_drawing = this.DM_SQUARE_RESIZE_TOP;
  1883. }
  1884. }
  1885. else if (this.is_drawing == this.DM_RECTANGLE_RESIZE_LEFT) {
  1886. this.fireEvent('onResizeArea', this.currentid);
  1887. xdiff = x - left;
  1888. if (width + (-1 * xdiff) > 0) {
  1889. //real resize left
  1890. this.areas[this.currentid].style.left = x + 1 + 'px';
  1891. this.setAreaSize(this.currentid, width + (-1 * xdiff), null);
  1892. }
  1893. else {
  1894. //jump to another state
  1895. this.memory[this.currentid].width = 0;
  1896. this.memory[this.currentid].left = x;
  1897. this.is_drawing = this.DM_RECTANGLE_RESIZE_RIGHT;
  1898. }
  1899. }
  1900. else if (this.is_drawing == this.DM_RECTANGLE_RESIZE_RIGHT) {
  1901. this.fireEvent('onResizeArea', this.currentid);
  1902. xdiff = x - left - width;
  1903. if ((width + (xdiff)) - 1 > 0) {
  1904. //real resize right
  1905. this.setAreaSize(this.currentid, (width + (xdiff)) - 1, null);
  1906. }
  1907. else {
  1908. //jump to another state
  1909. this.memory[this.currentid].width = 0;
  1910. this.memory[this.currentid].left = x;
  1911. this.is_drawing = this.DM_RECTANGLE_RESIZE_LEFT;
  1912. }
  1913. }
  1914. else if (this.is_drawing == this.DM_RECTANGLE_RESIZE_TOP) {
  1915. this.fireEvent('onResizeArea', this.currentid);
  1916. ydiff = y - top;
  1917. if ((height + (-1 * ydiff)) > 0) {
  1918. //real resize top
  1919. this.areas[this.currentid].style.top = y + 1 + 'px';
  1920. this.setAreaSize(this.currentid, null, (height + (-1 * ydiff)));
  1921. }
  1922. else {
  1923. //jump to another state
  1924. this.memory[this.currentid].height = 0;
  1925. this.memory[this.currentid].top = y;
  1926. this.is_drawing = this.DM_RECTANGLE_RESIZE_BOTTOM;
  1927. }
  1928. }
  1929. else if (this.is_drawing == this.DM_RECTANGLE_RESIZE_BOTTOM) {
  1930. this.fireEvent('onResizeArea', this.currentid);
  1931. ydiff = y - top - height;
  1932. if ((height + (ydiff)) - 1 > 0) {
  1933. //real resize bottom
  1934. this.setAreaSize(this.currentid, null, (height + (ydiff)) - 1);
  1935. }
  1936. else {
  1937. //jump to another state
  1938. this.memory[this.currentid].height = 0;
  1939. this.memory[this.currentid].top = y;
  1940. this.is_drawing = this.DM_RECTANGLE_RESIZE_TOP;
  1941. }
  1942. }
  1943. //repaint canvas elements
  1944. if (this.is_drawing) {
  1945. this._repaint(this.areas[this.currentid], this.config.CL_DRAW_SHAPE, x, y);
  1946. this._updatecoords(this.currentid);
  1947. }
  1948. };
  1949. /**
  1950. * EVENT HANDLER: Handles mouseup on the image.
  1951. * Handles dragging and resizing.
  1952. * @param e The event object.
  1953. */
  1954. imgmap.prototype.img_mouseup = function(e) {
  1955. if (this.viewmode === 1) {return;}//exit if preview mode
  1956. //console.log('img_mouseup');
  1957. //if (!this.props[this.currentid]) return;
  1958. var pos = this._getPos(this.pic);
  1959. // 3.3.3 fix
  1960. var x = (this.isMSIE) ? (window.event.x - this.pic.offsetLeft) : (e.clientX - pos.x);
  1961. var y = (this.isMSIE) ? (window.event.y - this.pic.offsetTop) : (e.clientY - pos.y);
  1962. x = x + this.pic_container.scrollLeft;
  1963. y = y + this.pic_container.scrollTop;
  1964. //for everything that is move or resize
  1965. if (this.is_drawing != this.DM_RECTANGLE_DRAW &&
  1966. this.is_drawing != this.DM_SQUARE_DRAW &&
  1967. this.is_drawing != this.DM_POLYGON_DRAW &&
  1968. this.is_drawing != this.DM_POLYGON_LASTDRAW &&
  1969. this.is_drawing != this.DM_BEZIER_DRAW &&
  1970. this.is_drawing != this.DM_BEZIER_LASTDRAW) {
  1971. //end dragging
  1972. this.draggedId = null;
  1973. //finish state
  1974. this.is_drawing = 0;
  1975. this.statusMessage(this.strings.READY);
  1976. this.relaxArea(this.currentid);
  1977. if (this.areas[this.currentid] == this._getLastArea()) {
  1978. //if (this.config.mode != "editor2") this.addNewArea();
  1979. return;
  1980. }
  1981. this.memory[this.currentid].downx = x;
  1982. this.memory[this.currentid].downy = y;
  1983. }
  1984. };
  1985. /**
  1986. * EVENT HANDLER: Handles mousedown on the image.
  1987. * Handles beggining or end of draw, or polygon/bezier point set.
  1988. * @param e The event object.
  1989. */
  1990. imgmap.prototype.img_mousedown = function(e) {
  1991. if (this.viewmode === 1) {return;}//exit if preview mode
  1992. if (!this.areas[this.currentid] && this.config.mode != "editor2") {return;}
  1993. //console.log('img_mousedown');
  1994. var pos = this._getPos(this.pic);
  1995. // 3.3.3 fix
  1996. var x = (this.isMSIE) ? (window.event.x - this.pic.offsetLeft) : (e.clientX - pos.x);
  1997. var y = (this.isMSIE) ? (window.event.y - this.pic.offsetTop) : (e.clientY - pos.y);
  1998. x = x + this.pic_container.scrollLeft;
  1999. y = y + this.pic_container.scrollTop;
  2000. // Handle the Shift state
  2001. if (!e) {
  2002. e = window.event;
  2003. }
  2004. if (e.shiftKey) {
  2005. if (this.is_drawing == this.DM_POLYGON_DRAW) {
  2006. this.is_drawing = this.DM_POLYGON_LASTDRAW;
  2007. }
  2008. else if (this.is_drawing == this.DM_BEZIER_DRAW) {
  2009. this.is_drawing = this.DM_BEZIER_LASTDRAW;
  2010. }
  2011. }
  2012. //console.log(this.is_drawing);
  2013. //this.statusMessage(x + ' - ' + y + ': ' + this.props[this.currentid].getElementsByTagName('select')[0].value);
  2014. if (this.is_drawing == this.DM_POLYGON_DRAW || this.is_drawing == this.DM_BEZIER_DRAW) {
  2015. //its not finish state yet
  2016. this.areas[this.currentid].xpoints[this.areas[this.currentid].xpoints.length] = x - 5;
  2017. this.areas[this.currentid].ypoints[this.areas[this.currentid].ypoints.length] = y - 5;
  2018. this.memory[this.currentid].downx = x;
  2019. this.memory[this.currentid].downy = y;
  2020. return;
  2021. }
  2022. else if (this.is_drawing && this.is_drawing != this.DM_POLYGON_DRAW && this.is_drawing != this.DM_BEZIER_DRAW) {
  2023. //finish any other state
  2024. if (this.is_drawing == this.DM_POLYGON_LASTDRAW || this.is_drawing == this.DM_BEZIER_LASTDRAW) {
  2025. //add last controlpoint and update coords
  2026. this.areas[this.currentid].xpoints[this.areas[this.currentid].xpoints.length] = x - 5;
  2027. this.areas[this.currentid].ypoints[this.areas[this.currentid].ypoints.length] = y - 5;
  2028. this._updatecoords(this.currentid);
  2029. this.is_drawing = 0;
  2030. this._polygonshrink(this.areas[this.currentid]);
  2031. }
  2032. this.is_drawing = 0;
  2033. this.statusMessage(this.strings.READY);
  2034. this.relaxArea(this.currentid);
  2035. if (this.areas[this.currentid] == this._getLastArea()) {
  2036. //editor mode adds next area automatically
  2037. if (this.config.mode != "editor2") {this.addNewArea();}
  2038. return;
  2039. }
  2040. return;
  2041. }
  2042. if (this.config.mode == "editor2") {
  2043. if (!this.nextShape) {return;}
  2044. this.addNewArea();
  2045. //console.log("init: " + this.nextShape);
  2046. this.initArea(this.currentid, this.nextShape);
  2047. }
  2048. else if (this.areas[this.currentid].shape == 'undefined' || this.areas[this.currentid].shape == 'poly') {
  2049. //var shape = (this.props[this.currentid]) ? this.props[this.currentid].getElementsByTagName('select')[0].value : this.nextShape;
  2050. var shape = this.nextShape;
  2051. if (!shape) {shape = 'rect';}
  2052. //console.log("init: " + shape);
  2053. this.initArea(this.currentid, shape);
  2054. }
  2055. if (this.areas[this.currentid].shape == 'poly') {
  2056. this.is_drawing = this.DM_POLYGON_DRAW;
  2057. this.statusMessage(this.strings.POLYGON_DRAW);
  2058. this.areas[this.currentid].style.left = x + 'px';
  2059. this.areas[this.currentid].style.top = y + 'px';
  2060. this.areas[this.currentid].style.width = 0;
  2061. this.areas[this.currentid].style.height = 0;
  2062. this.areas[this.currentid].xpoints = [];
  2063. this.areas[this.currentid].ypoints = [];
  2064. this.areas[this.currentid].xpoints[0] = x;
  2065. this.areas[this.currentid].ypoints[0] = y;
  2066. }
  2067. else if (this.areas[this.currentid].shape == 'bezier1') {
  2068. this.is_drawing = this.DM_BEZIER_DRAW;
  2069. this.statusMessage(this.strings.BEZIER_DRAW);
  2070. this.areas[this.currentid].style.left = x + 'px';
  2071. this.areas[this.currentid].style.top = y + 'px';
  2072. this.areas[this.currentid].style.width = 0;
  2073. this.areas[this.currentid].style.height = 0;
  2074. this.areas[this.currentid].xpoints = [];
  2075. this.areas[this.currentid].ypoints = [];
  2076. this.areas[this.currentid].xpoints[0] = x;
  2077. this.areas[this.currentid].ypoints[0] = y;
  2078. }
  2079. else if (this.areas[this.currentid].shape == 'rect') {
  2080. this.is_drawing = this.DM_RECTANGLE_DRAW;
  2081. this.statusMessage(this.strings.RECTANGLE_DRAW);
  2082. this.areas[this.currentid].style.left = x + 'px';
  2083. this.areas[this.currentid].style.top = y + 'px';
  2084. this.areas[this.currentid].style.width = 0;
  2085. this.areas[this.currentid].style.height = 0;
  2086. }
  2087. else if (this.areas[this.currentid].shape == 'circle') {
  2088. this.is_drawing = this.DM_SQUARE_DRAW;
  2089. this.statusMessage(this.strings.SQUARE_DRAW);
  2090. this.areas[this.currentid].style.left = x + 'px';
  2091. this.areas[this.currentid].style.top = y + 'px';
  2092. this.areas[this.currentid].style.width = 0;
  2093. this.areas[this.currentid].style.height = 0;
  2094. }
  2095. this._setBorder(this.currentid, 'DRAW');
  2096. this.memory[this.currentid].downx = x;
  2097. this.memory[this.currentid].downy = y;
  2098. };
  2099. /**
  2100. * Highlights a given area.
  2101. * Sets opacity and repaints.
  2102. * @date 2007.12.28. 18:23:00
  2103. * @param id The id of the area to blur.
  2104. * @param flag Modifier, possible values: grad - for gradual fade in
  2105. */
  2106. imgmap.prototype.highlightArea = function(id, flag) {
  2107. if (this.is_drawing) {return;}//exit if in drawing state
  2108. if (this.areas[id] && this.areas[id].shape != 'undefined') {
  2109. //area exists - highlight it
  2110. this.fireEvent('onFocusArea', this.areas[id]);
  2111. this._setBorder(id, 'HIGHLIGHT');
  2112. var opacity = this.config.highlight_opacity;
  2113. if (flag == 'grad') {
  2114. //apply gradient opacity
  2115. opacity = '-' + opacity;
  2116. }
  2117. this._setopacity(this.areas[id], this.config.CL_HIGHLIGHT_BG, opacity);
  2118. this._repaint(this.areas[id], this.config.CL_HIGHLIGHT_SHAPE);
  2119. }
  2120. };
  2121. /**
  2122. * Blurs a given area.
  2123. * Sets opacity and repaints.
  2124. * @date 2007.12.28. 18:23:26
  2125. * @param id The id of the area to blur.
  2126. * @param flag Modifier, possible values: grad - for gradual fade out
  2127. */
  2128. imgmap.prototype.blurArea = function(id, flag) {
  2129. if (this.is_drawing) {return;}//exit if in drawing state
  2130. if (this.areas[id] && this.areas[id].shape != 'undefined') {
  2131. //area exists - fade it back
  2132. this.fireEvent('onBlurArea', this.areas[id]);
  2133. this._setBorder(id, 'NORM');
  2134. var opacity = this.config.norm_opacity;
  2135. if (flag == 'grad') {
  2136. //apply gradient opacity
  2137. opacity = '-' + opacity;
  2138. }
  2139. this._setopacity(this.areas[id], this.config.CL_NORM_BG, opacity);
  2140. this._repaint(this.areas[id], this.config.CL_NORM_SHAPE);
  2141. }
  2142. };
  2143. /**
  2144. * EVENT HANDLER: Handles event of mousemove on imgmap areas.
  2145. * - changes cursor depending where we are inside the area (buggy in opera)
  2146. * - handles area resize
  2147. * - handles area move
  2148. * @url http://evolt.org/article/Mission_Impossible_mouse_position/17/23335/index.html
  2149. * @url http://my.opera.com/community/forums/topic.dml?id=239498&t=1217158015&page=1
  2150. * @author adam
  2151. * @param e The event object.
  2152. */
  2153. imgmap.prototype.area_mousemove = function(e) {
  2154. if (this.viewmode === 1) {return;}//exit if preview mode
  2155. if (!this.is_drawing) {
  2156. var obj = (this.isMSIE) ? window.event.srcElement : e.currentTarget;
  2157. if (obj.tagName == 'DIV') {
  2158. //do this because of label
  2159. obj = obj.parentNode;
  2160. }
  2161. if (obj.tagName == 'image' || obj.tagName == 'group' ||
  2162. obj.tagName == 'shape' || obj.tagName == 'stroke') {
  2163. //do this because of excanvas
  2164. obj = obj.parentNode.parentNode;
  2165. }
  2166. //opera fix - adam - 04-12-2007 23:14:05
  2167. if (this.isOpera) {
  2168. e.layerX = e.offsetX;
  2169. e.layerY = e.offsetY;
  2170. }
  2171. var xdiff = (this.isMSIE) ? (window.event.offsetX) : (e.layerX);
  2172. var ydiff = (this.isMSIE) ? (window.event.offsetY) : (e.layerY);
  2173. //this.log(obj.aid + ' : ' + xdiff + ',' + ydiff);
  2174. var resizable = (obj.shape == 'rect' || obj.shape == 'circle');
  2175. if (resizable && xdiff < 6 && ydiff > 6) {
  2176. //move left
  2177. obj.style.cursor = 'w-resize';
  2178. }
  2179. else if (resizable && xdiff > parseInt(obj.style.width, 10) - 6 && ydiff > 6) {
  2180. //move right
  2181. obj.style.cursor = 'e-resize';
  2182. }
  2183. else if (resizable && xdiff > 6 && ydiff < 6) {
  2184. //move top
  2185. obj.style.cursor = 'n-resize';
  2186. }
  2187. else if (resizable && ydiff > parseInt(obj.style.height, 10) - 6 && xdiff > 6) {
  2188. //move bottom
  2189. obj.style.cursor = 's-resize';
  2190. }
  2191. else {
  2192. //move all
  2193. obj.style.cursor = 'move';
  2194. }
  2195. if (obj.aid != this.draggedId) {
  2196. //not dragged or different
  2197. if (obj.style.cursor == 'move') {obj.style.cursor = 'default';}
  2198. return;
  2199. }
  2200. //moved here from mousedown
  2201. if (xdiff < 6 && ydiff > 6) {
  2202. //move left
  2203. if (this.areas[this.currentid].shape == 'circle') {
  2204. this.is_drawing = this.DM_SQUARE_RESIZE_LEFT;
  2205. this.statusMessage(this.strings.SQUARE_RESIZE_LEFT);
  2206. }
  2207. else if (this.areas[this.currentid].shape == 'rect') {
  2208. this.is_drawing = this.DM_RECTANGLE_RESIZE_LEFT;
  2209. this.statusMessage(this.strings.RECTANGLE_RESIZE_LEFT);
  2210. }
  2211. }
  2212. else if (xdiff > parseInt(this.areas[this.currentid].style.width, 10) - 6 && ydiff > 6) {
  2213. //move right
  2214. if (this.areas[this.currentid].shape == 'circle') {
  2215. this.is_drawing = this.DM_SQUARE_RESIZE_RIGHT;
  2216. this.statusMessage(this.strings.SQUARE_RESIZE_RIGHT);
  2217. }
  2218. else if (this.areas[this.currentid].shape == 'rect') {
  2219. this.is_drawing = this.DM_RECTANGLE_RESIZE_RIGHT;
  2220. this.statusMessage(this.strings.RECTANGLE_RESIZE_RIGHT);
  2221. }
  2222. }
  2223. else if (xdiff > 6 && ydiff < 6) {
  2224. //move top
  2225. if (this.areas[this.currentid].shape == 'circle') {
  2226. this.is_drawing = this.DM_SQUARE_RESIZE_TOP;
  2227. this.statusMessage(this.strings.SQUARE_RESIZE_TOP);
  2228. }
  2229. else if (this.areas[this.currentid].shape == 'rect') {
  2230. this.is_drawing = this.DM_RECTANGLE_RESIZE_TOP;
  2231. this.statusMessage(this.strings.RECTANGLE_RESIZE_TOP);
  2232. }
  2233. }
  2234. else if (ydiff > parseInt(this.areas[this.currentid].style.height, 10) - 6 && xdiff > 6) {
  2235. //move bottom
  2236. if (this.areas[this.currentid].shape == 'circle') {
  2237. this.is_drawing = this.DM_SQUARE_RESIZE_BOTTOM;
  2238. this.statusMessage(this.strings.SQUARE_RESIZE_BOTTOM);
  2239. }
  2240. else if (this.areas[this.currentid].shape == 'rect') {
  2241. this.is_drawing = this.DM_RECTANGLE_RESIZE_BOTTOM;
  2242. this.statusMessage(this.strings.RECTANGLE_RESIZE_BOTTOM);
  2243. }
  2244. }
  2245. else/*if (xdiff < 10 && ydiff < 10 ) */{
  2246. //move all
  2247. if (this.areas[this.currentid].shape == 'circle') {
  2248. this.is_drawing = this.DM_SQUARE_MOVE;
  2249. this.statusMessage(this.strings.SQUARE_MOVE);
  2250. this.memory[this.currentid].rdownx = xdiff;
  2251. this.memory[this.currentid].rdowny = ydiff;
  2252. }
  2253. else if (this.areas[this.currentid].shape == 'rect') {
  2254. this.is_drawing = this.DM_RECTANGLE_MOVE;
  2255. this.statusMessage(this.strings.RECTANGLE_MOVE);
  2256. this.memory[this.currentid].rdownx = xdiff;
  2257. this.memory[this.currentid].rdowny = ydiff;
  2258. }
  2259. else if (this.areas[this.currentid].shape == 'poly' || this.areas[this.currentid].shape == 'bezier1') {
  2260. if (this.areas[this.currentid].xpoints) {
  2261. for (var i=0, le = this.areas[this.currentid].xpoints.length; i<le; i++) {
  2262. this.memory[this.currentid].xpoints[i] = this.areas[this.currentid].xpoints[i];
  2263. this.memory[this.currentid].ypoints[i] = this.areas[this.currentid].ypoints[i];
  2264. }
  2265. }
  2266. if (this.areas[this.currentid].shape == 'poly') {
  2267. this.is_drawing = this.DM_POLYGON_MOVE;
  2268. this.statusMessage(this.strings.POLYGON_MOVE);
  2269. }
  2270. else if (this.areas[this.currentid].shape == 'bezier1') {
  2271. this.is_drawing = this.DM_BEZIER_MOVE;
  2272. this.statusMessage(this.strings.BEZIER_MOVE);
  2273. }
  2274. this.memory[this.currentid].rdownx = xdiff;
  2275. this.memory[this.currentid].rdowny = ydiff;
  2276. }
  2277. }
  2278. //common memory settings (preparing to move or resize)
  2279. this.memory[this.currentid].width = parseInt(this.areas[this.currentid].style.width, 10);
  2280. this.memory[this.currentid].height = parseInt(this.areas[this.currentid].style.height, 10);
  2281. this.memory[this.currentid].top = parseInt(this.areas[this.currentid].style.top, 10);
  2282. this.memory[this.currentid].left = parseInt(this.areas[this.currentid].style.left, 10);
  2283. this._setBorder(this.currentid, 'DRAW');
  2284. this._setopacity(this.areas[this.currentid], this.config.CL_DRAW_BG, this.config.draw_opacity);
  2285. }
  2286. else {
  2287. //if drawing and not ie, have to propagate to image event
  2288. this.img_mousemove(e);
  2289. }
  2290. };
  2291. /**
  2292. * EVENT HANDLER: Handles event of mouseup on imgmap areas.
  2293. * Basically clears draggedId.
  2294. * @author adam
  2295. * @param e The event object
  2296. */
  2297. imgmap.prototype.area_mouseup = function(e) {
  2298. if (this.viewmode === 1) {return;}//exit if preview mode
  2299. //console.log('area_mouseup');
  2300. if (!this.is_drawing) {
  2301. var obj = (this.isMSIE) ? window.event.srcElement : e.currentTarget;
  2302. if (obj.tagName == 'DIV') {
  2303. //do this because of label
  2304. obj = obj.parentNode;
  2305. }
  2306. if (obj.tagName == 'image' || obj.tagName == 'group' ||
  2307. obj.tagName == 'shape' || obj.tagName == 'stroke') {
  2308. //do this because of excanvas
  2309. obj = obj.parentNode.parentNode;
  2310. }
  2311. if (this.areas[this.currentid] != obj) {
  2312. //trying to draw on a different canvas,switch to this one
  2313. if (typeof obj.aid == 'undefined') {
  2314. this.log('Cannot identify target area', 1);
  2315. return;
  2316. }
  2317. //this.form_selectRow(obj.aid, true);
  2318. //this.currentid = obj.aid;
  2319. }
  2320. this.draggedId = null;
  2321. }
  2322. else {
  2323. //if drawing and not ie, have to propagate to image event
  2324. //console.log('propup');
  2325. this.img_mouseup(e);
  2326. }
  2327. };
  2328. /**
  2329. * EVENT HANDLER: Handles event of mouseover on imgmap areas.
  2330. * Calls gradual highlight on the given area.
  2331. * @author adam
  2332. * @param e The event object
  2333. */
  2334. imgmap.prototype.area_mouseover = function(e) {
  2335. if (this.viewmode === 1 && this.config.mode !== 'highlighter_spawn') {return;}//exit if preview mode
  2336. if (!this.is_drawing) {
  2337. //this.log('area_mouseover');
  2338. //identify source object
  2339. var obj = (this.isMSIE) ? window.event.srcElement : e.currentTarget;
  2340. if (obj.tagName == 'DIV') {
  2341. //do this because of label
  2342. obj = obj.parentNode;
  2343. }
  2344. if (obj.tagName == 'image' || obj.tagName == 'group' ||
  2345. obj.tagName == 'shape' || obj.tagName == 'stroke') {
  2346. //do this because of excanvas
  2347. obj = obj.parentNode.parentNode;
  2348. }
  2349. /*
  2350. //switch to hovered area
  2351. if (this.areas[this.currentid] != obj) {
  2352. //trying to draw on a different canvas, switch to this one
  2353. if (typeof obj.aid == 'undefined') {
  2354. this.log('Cannot identify target area', 1);
  2355. return;
  2356. }
  2357. this.form_selectRow(obj.aid, true);
  2358. this.currentid = obj.aid;
  2359. }
  2360. */
  2361. this.highlightArea(obj.aid, 'grad');
  2362. }
  2363. };
  2364. /**
  2365. * EVENT HANDLER: Handles event of mouseout on imgmap areas.
  2366. * Calls gradient blur on the given area.
  2367. * @author adam
  2368. * @param e The event object
  2369. */
  2370. imgmap.prototype.area_mouseout = function(e) {
  2371. if (this.viewmode === 1 && this.config.mode !== 'highlighter_spawn') {return;}//exit if preview mode
  2372. if (!this.is_drawing) {
  2373. //this.log('area_mouseout');
  2374. //identify source object
  2375. var obj = (this.isMSIE) ? window.event.srcElement : e.currentTarget;
  2376. if (obj.tagName == 'DIV') {
  2377. //do this because of label
  2378. obj = obj.parentNode;
  2379. }
  2380. if (obj.tagName == 'image' || obj.tagName == 'group' ||
  2381. obj.tagName == 'shape' || obj.tagName == 'stroke') {
  2382. //do this because of excanvas
  2383. obj = obj.parentNode.parentNode;
  2384. }
  2385. this.blurArea(obj.aid, 'grad');
  2386. }
  2387. };
  2388. /**
  2389. * EVENT HANDLER: Handles event of double click on imgmap areas.
  2390. * Basically only fires the custom callback.
  2391. * @author Colin Bell
  2392. * @param e The event object
  2393. */
  2394. imgmap.prototype.area_dblclick = function(e) {
  2395. if (this.viewmode === 1) {return;}//exit if preview mode
  2396. //console.log('area_dblclick');
  2397. if (!this.is_drawing) {
  2398. var obj = (this.isMSIE) ? window.event.srcElement : e.currentTarget;
  2399. if (obj.tagName == 'DIV') {
  2400. //do this because of label
  2401. obj = obj.parentNode;
  2402. }
  2403. if (obj.tagName == 'image' || obj.tagName == 'group' ||
  2404. obj.tagName == 'shape' || obj.tagName == 'stroke') {
  2405. //do this because of excanvas
  2406. obj = obj.parentNode.parentNode;
  2407. }
  2408. if (this.areas[this.currentid] != obj) {
  2409. //trying to draw on a different canvas, switch to this one
  2410. if (typeof obj.aid == 'undefined') {
  2411. this.log('Cannot identify target area', 1);
  2412. return;
  2413. }
  2414. this.currentid = obj.aid;
  2415. }
  2416. this.fireEvent('onDblClickArea', this.areas[this.currentid]);
  2417. //stop event propagation to document level
  2418. if (this.isMSIE) {
  2419. window.event.cancelBubble = true;
  2420. }
  2421. else {
  2422. e.stopPropagation();
  2423. }
  2424. }
  2425. };
  2426. /**
  2427. * EVENT HANDLER: Handles event of mousedown on imgmap areas.
  2428. * Sets the variables draggedid, selectedid and currentid to the given area.
  2429. * @author adam
  2430. * @param e The event object
  2431. */
  2432. imgmap.prototype.area_mousedown = function(e) {
  2433. if (this.viewmode === 1 && this.config.mode !== 'highlighter_spawn') {return;}//exit if preview mode
  2434. //console.log('area_mousedown');
  2435. if (!this.is_drawing) {
  2436. var obj = (this.isMSIE) ? window.event.srcElement : e.currentTarget;
  2437. if (obj.tagName == 'DIV') {
  2438. //do this because of label
  2439. obj = obj.parentNode;
  2440. }
  2441. if (obj.tagName == 'image' || obj.tagName == 'group' ||
  2442. obj.tagName == 'shape' || obj.tagName == 'stroke') {
  2443. //do this because of excanvas
  2444. obj = obj.parentNode.parentNode;
  2445. }
  2446. if (this.areas[this.currentid] != obj) {
  2447. //trying to draw on a different canvas, switch to this one
  2448. if (typeof obj.aid == 'undefined') {
  2449. this.log('Cannot identify target area', 1);
  2450. return;
  2451. }
  2452. this.currentid = obj.aid;
  2453. }
  2454. //this.log('selected = '+this.currentid);
  2455. this.draggedId = this.currentid;
  2456. this.selectedId = this.currentid;
  2457. this.fireEvent('onSelectArea', this.areas[this.currentid]);
  2458. //stop event propagation to document level
  2459. if (this.isMSIE) {
  2460. window.event.cancelBubble = true;
  2461. }
  2462. else {
  2463. e.stopPropagation();
  2464. }
  2465. }
  2466. else {
  2467. //if drawing and not ie, have to propagate to image event
  2468. //console.log('propdown');
  2469. this.img_mousedown(e);
  2470. }
  2471. };
  2472. /**
  2473. * EVENT HANDLER: Handles event 'keydown' on document.
  2474. * Handles SHIFT hold while drawing.
  2475. * Note: Safari doesn't generate keyboard events for modifiers:
  2476. * @url http://bugs.webkit.org/show_bug.cgi?id=11696
  2477. * @author adam
  2478. * @param e The event object
  2479. */
  2480. imgmap.prototype.doc_keydown = function(e) {
  2481. if (this.viewmode === 1) {return;}//exit if preview mode
  2482. var key = (this.isMSIE) ? event.keyCode : e.keyCode;
  2483. //console.log(key);
  2484. if (key == 46) {
  2485. //delete key pressed
  2486. if (this.selectedId !== null && !this.is_drawing) {this.removeArea(this.selectedId);}
  2487. }
  2488. else if (key == 16) {
  2489. //shift key pressed
  2490. if (this.is_drawing == this.DM_RECTANGLE_DRAW) {
  2491. this.is_drawing = this.DM_SQUARE_DRAW;
  2492. this.statusMessage(this.strings.SQUARE2_DRAW);
  2493. }
  2494. }
  2495. };
  2496. /**
  2497. * EVENT HANDLER: Handles event 'keyup' on document.
  2498. * Handles SHIFT release while drawing.
  2499. * @author adam
  2500. * @param e The event object
  2501. */
  2502. imgmap.prototype.doc_keyup = function(e) {
  2503. var key = (this.isMSIE) ? event.keyCode : e.keyCode;
  2504. //alert(key);
  2505. if (key == 16) {
  2506. //shift key released
  2507. if (this.is_drawing == this.DM_SQUARE_DRAW && this.areas[this.currentid].shape == 'rect') {
  2508. //not for circle!
  2509. this.is_drawing = this.DM_RECTANGLE_DRAW;
  2510. this.statusMessage(this.strings.RECTANGLE_DRAW);
  2511. }
  2512. }
  2513. };
  2514. /**
  2515. * EVENT HANDLER: Handles event 'mousedown' on document.
  2516. * @author adam
  2517. * @param e The event object
  2518. */
  2519. imgmap.prototype.doc_mousedown = function(e) {
  2520. if (this.viewmode === 1) {return;}//exit if preview mode
  2521. if (!this.is_drawing) {
  2522. this.selectedId = null;
  2523. }
  2524. };
  2525. /**
  2526. * Get the real position of the element.
  2527. * Deal with browser differences when trying to get the position of an area.
  2528. * @param element The element you want the position of.
  2529. * @return An object with x and y members.
  2530. */
  2531. imgmap.prototype._getPos = function(element) {
  2532. var xpos = 0;
  2533. var ypos = 0;
  2534. if (element) {
  2535. var elementOffsetParent = element.offsetParent;
  2536. // If the element has an offset parent
  2537. if (elementOffsetParent) {
  2538. // While there is an offset parent
  2539. while ((elementOffsetParent = element.offsetParent)) {
  2540. //offset might give negative in opera when the image is scrolled
  2541. if (element.offsetLeft > 0) {xpos += element.offsetLeft;}
  2542. if (element.offsetTop > 0) {ypos += element.offsetTop;}
  2543. element = elementOffsetParent;
  2544. }
  2545. // handle positioned element in Chrome
  2546. if (element.offsetLeft > 0) {xpos += element.offsetLeft;}
  2547. if (element.offsetTop > 0) {ypos += element.offsetTop;}
  2548. }
  2549. else {
  2550. xpos = element.offsetLeft;
  2551. ypos = element.offsetTop;
  2552. }
  2553. }
  2554. return {x: xpos, y: ypos};
  2555. };
  2556. /**
  2557. * Gets the last (visible and editable) area.
  2558. * @author Adam Maschek (adam.maschek(at)gmail.com)
  2559. * @date 2006-06-15 16:34:51
  2560. * @returns The last area object or null.
  2561. */
  2562. imgmap.prototype._getLastArea = function() {
  2563. for (var i = this.areas.length-1; i>=0; i--) {
  2564. if (this.areas[i]) {
  2565. return this.areas[i];
  2566. }
  2567. }
  2568. return null;
  2569. };
  2570. /**
  2571. * Parses cssText to single style declarations.
  2572. * @author adam
  2573. * @date 25-09-2007 18:19:51
  2574. * @param obj The DOM object to apply styles on.
  2575. * @param cssText The css declarations to apply.
  2576. */
  2577. imgmap.prototype.assignCSS = function(obj, cssText) {
  2578. var parts = cssText.split(';');
  2579. for (var i = 0; i < parts.length; i++) {
  2580. var p = parts[i].split(':');
  2581. //we need to camelcase by - signs
  2582. var pp = this.trim(p[0]).split('-');
  2583. var prop = pp[0];
  2584. for (var j = 1; j < pp.length; j++) {
  2585. //replace first letters to uppercase
  2586. prop+= pp[j].replace(/^\w/, pp[j].substring(0,1).toUpperCase());
  2587. }
  2588. obj.style[this.trim(prop)] = this.trim(p[1]);
  2589. }
  2590. };
  2591. /**
  2592. * To fire callback hooks on custom events, passing them the object of the event.
  2593. * @author adam
  2594. * @date 13-10-2007 15:24:49
  2595. * @param evt The type of event
  2596. * @param obj The object of the event. (can be an id, a string, an object, whatever is most relevant)
  2597. */
  2598. imgmap.prototype.fireEvent = function(evt, obj) {
  2599. //this.log("Firing event: " + evt);
  2600. if (typeof this.config.custom_callbacks[evt] == 'function') {
  2601. return this.config.custom_callbacks[evt](obj);
  2602. }
  2603. };
  2604. /**
  2605. * To set area dimensions.
  2606. * This is needed to achieve the same result in all browsers.
  2607. * @author adam
  2608. * @date 10-12-2007 22:29:41
  2609. * @param id The id of the area (canvas) to resize.
  2610. * @param w The desired width in pixels.
  2611. * @param h The desired height in pixels.
  2612. */
  2613. imgmap.prototype.setAreaSize = function(id, w, h) {
  2614. if (id === null) {id = this.currentid;}
  2615. if (w !== null) {
  2616. this.areas[id].width = w;
  2617. this.areas[id].style.width = (w) + 'px';
  2618. this.areas[id].setAttribute('width', w);
  2619. }
  2620. if (h !== null) {
  2621. this.areas[id].height = h;
  2622. this.areas[id].style.height = (h) + 'px';
  2623. this.areas[id].setAttribute('height', h);
  2624. }
  2625. };
  2626. /**
  2627. * Tries to detect preferred language of user.
  2628. * @date 2007.12.28. 15:43:46
  2629. * @return The two byte language code. (We dont care now for pt-br, etc.)
  2630. */
  2631. imgmap.prototype.detectLanguage = function() {
  2632. var lang;
  2633. if (navigator.userLanguage) {
  2634. lang = navigator.userLanguage.toLowerCase();
  2635. }
  2636. else if (navigator.language) {
  2637. lang = navigator.language.toLowerCase();
  2638. }
  2639. else {
  2640. return this.config.defaultLang;
  2641. }
  2642. //this.log(lang, 2);
  2643. if (lang.length >= 2) {
  2644. lang = lang.substring(0,2);
  2645. return lang;
  2646. }
  2647. return this.config.defaultLang;
  2648. };
  2649. /**
  2650. * Disable selection on a given object.
  2651. * This is especially useful in Safari, where dragging around areas
  2652. * keeps selecting all sorts of things.
  2653. * @author Bret Taylor
  2654. * @url http://ajaxcookbook.org/disable-text-selection/
  2655. * @date 27-07-2008 1:57:45
  2656. * @param element The DOM element on which you want to disable selection.
  2657. */
  2658. imgmap.prototype.disableSelection = function(element) {
  2659. if (typeof element == 'undefined' || !element) {return false;}
  2660. if (typeof element.onselectstart != "undefined") {
  2661. element.onselectstart = function() {
  2662. return false;
  2663. };
  2664. }
  2665. if (typeof element.unselectable != "undefined") {
  2666. element.unselectable = "on";
  2667. }
  2668. if (typeof element.style.MozUserSelect != "undefined") {
  2669. element.style.MozUserSelect = "none";
  2670. }
  2671. };
  2672. /**
  2673. * @date 11-02-2007 19:57:05
  2674. * @url http://www.deepwood.net/writing/method-references.html.utf8
  2675. * @author Daniel Brockman
  2676. * @addon
  2677. */
  2678. Function.prototype.bind = function(object) {
  2679. var method = this;
  2680. return function () {
  2681. return method.apply(object, arguments);
  2682. };
  2683. };
  2684. /**
  2685. * Trims a string.
  2686. * Changed not to extend String but use own function for better compatibility.
  2687. * @param str The string to trim.
  2688. */
  2689. imgmap.prototype.trim = function(str) {
  2690. return str.replace(/^\s+|\s+$/g, '');
  2691. };
  2692. /**
  2693. * Spawn an imgmap object for each imagemap found in the document.
  2694. * This is used for highlighter mode only.
  2695. * @param config An imgmap config object
  2696. */
  2697. function imgmap_spawnObjects(config) {
  2698. //console.log('spawnobjects');
  2699. var maps = document.getElementsByTagName('map');
  2700. var imgs = document.getElementsByTagName('img');
  2701. var imaps = [];
  2702. var imapn;
  2703. //console.log(maps.length);
  2704. for (var i=0, le=maps.length; i<le; i++) {
  2705. for (var j=0, le2=imgs.length; j<le2; j++) {
  2706. //console.log(i);
  2707. // console.log(maps[i].name);
  2708. // console.log(imgs[j].getAttribute('usemap'));
  2709. if ('#' + maps[i].name == imgs[j].getAttribute('usemap')) {
  2710. //we found one matching pair
  2711. // console.log(maps[i]);
  2712. config.mode = 'highlighter_spawn';
  2713. imapn = new imgmap(config);
  2714. //imapn.setup(config);
  2715. imapn.useImage(imgs[j]);
  2716. imapn.setMapHTML(maps[i]);
  2717. imapn.viewmode = 1;
  2718. imaps.push(imapn);
  2719. }
  2720. }
  2721. }
  2722. }
  2723. //global instance?
  2724. //imgmap_spawnObjects();?