ace-elements.js 70 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454
  1. /*!
  2. * Ace v1.3.2
  3. */
  4. if (typeof jQuery === 'undefined') { throw new Error('Ace\'s JavaScript requires jQuery') }
  5. /**
  6. <b>Ace custom scroller</b>. It is not as feature-rich as plugins such as NiceScroll but it's good enough for most cases.
  7. */
  8. (function($ , undefined) {
  9. var Ace_Scroll = function(element , _settings) {
  10. var self = this;
  11. var settings = $.extend({}, $.fn.ace_scroll.defaults, _settings);
  12. this.size = 0;
  13. this.lock = false;
  14. this.lock_anyway = false;
  15. this.$element = $(element);
  16. this.element = element;
  17. var vertical = true;
  18. var disabled = false;
  19. var active = false;
  20. var created = false;
  21. var $content_wrap = null, content_wrap = null;
  22. var $track = null, $bar = null, track = null, bar = null;
  23. var bar_style = null;
  24. var bar_size = 0, bar_pos = 0, bar_max_pos = 0, bar_size_2 = 0, move_bar = true;
  25. var reset_once = false;
  26. var styleClass = '';
  27. var trackFlip = false;//vertical on left or horizontal on top
  28. var trackSize = 0;
  29. var css_pos,
  30. css_size,
  31. max_css_size,
  32. client_size,
  33. scroll_direction,
  34. scroll_size;
  35. var ratio = 1;
  36. var inline_style = false;
  37. var mouse_track = false;
  38. var mouse_release_target = 'onmouseup' in window ? window : 'html';
  39. var dragEvent = settings.dragEvent || false;
  40. var trigger_scroll = _settings.scrollEvent || false;
  41. var detached = settings.detached || false;//when detached, hideOnIdle as well?
  42. var updatePos = settings.updatePos || false;//default is true
  43. var hideOnIdle = settings.hideOnIdle || false;
  44. var hideDelay = settings.hideDelay || 1500;
  45. var insideTrack = false;//used to hide scroll track when mouse is up and outside of track
  46. var observeContent = settings.observeContent || false;
  47. var prevContentSize = 0;
  48. var is_dirty = true;//to prevent consecutive 'reset' calls
  49. this.create = function(_settings) {
  50. if(created) return;
  51. //if(disabled) return;
  52. if(_settings) settings = $.extend({}, $.fn.ace_scroll.defaults, _settings);
  53. this.size = parseInt(this.$element.attr('data-size')) || settings.size || 200;
  54. vertical = !settings['horizontal'];
  55. css_pos = vertical ? 'top' : 'left';//'left' for horizontal
  56. css_size = vertical ? 'height' : 'width';//'width' for horizontal
  57. max_css_size = vertical ? 'maxHeight' : 'maxWidth';
  58. client_size = vertical ? 'clientHeight' : 'clientWidth';
  59. scroll_direction = vertical ? 'scrollTop' : 'scrollLeft';
  60. scroll_size = vertical ? 'scrollHeight' : 'scrollWidth';
  61. this.$element.addClass('ace-scroll');
  62. if(this.$element.css('position') == 'static') {
  63. inline_style = this.element.style.position;
  64. this.element.style.position = 'relative';
  65. } else inline_style = false;
  66. var scroll_bar = null;
  67. if(!detached) {
  68. this.$element.wrapInner('<div class="scroll-content" />');
  69. this.$element.prepend('<div class="scroll-track"><div class="scroll-bar"></div></div>');
  70. }
  71. else {
  72. scroll_bar = $('<div class="scroll-track scroll-detached"><div class="scroll-bar"></div></div>').appendTo('body');
  73. }
  74. $content_wrap = this.$element;
  75. if(!detached) $content_wrap = this.$element.find('.scroll-content').eq(0);
  76. if(!vertical) $content_wrap.wrapInner('<div />');
  77. content_wrap = $content_wrap.get(0);
  78. if(detached) {
  79. //set position for detached scrollbar
  80. $track = scroll_bar;
  81. setTrackPos();
  82. }
  83. else $track = this.$element.find('.scroll-track').eq(0);
  84. $bar = $track.find('.scroll-bar').eq(0);
  85. track = $track.get(0);
  86. bar = $bar.get(0);
  87. bar_style = bar.style;
  88. //add styling classes and horizontalness
  89. if(!vertical) $track.addClass('scroll-hz');
  90. if(settings.styleClass) {
  91. styleClass = settings.styleClass;
  92. $track.addClass(styleClass);
  93. trackFlip = !!styleClass.match(/scroll\-left|scroll\-top/);
  94. }
  95. //calculate size of track!
  96. if(trackSize == 0) {
  97. $track.show();
  98. getTrackSize();
  99. }
  100. $track.hide();
  101. //if(!touchDrag) {
  102. $track.on('mousedown', mouse_down_track);
  103. $bar.on('mousedown', mouse_down_bar);
  104. //}
  105. $content_wrap.on('scroll', function() {
  106. if(move_bar) {
  107. bar_pos = parseInt(Math.round(this[scroll_direction] * ratio));
  108. bar_style[css_pos] = bar_pos + 'px';
  109. }
  110. move_bar = false;
  111. if(trigger_scroll) this.$element.trigger('scroll', [content_wrap]);
  112. })
  113. if(settings.mouseWheel) {
  114. this.lock = settings.mouseWheelLock;
  115. this.lock_anyway = settings.lockAnyway;
  116. //mousewheel library available?
  117. this.$element.on(!!$.event.special.mousewheel ? 'mousewheel.ace_scroll' : 'mousewheel.ace_scroll DOMMouseScroll.ace_scroll', function(event) {
  118. if(disabled) return;
  119. checkContentChanges(true);
  120. if(!active) return !self.lock_anyway;
  121. if(mouse_track) {
  122. mouse_track = false;
  123. $('html').off('.ace_scroll')
  124. $(mouse_release_target).off('.ace_scroll');
  125. if(dragEvent) self.$element.trigger('drag.end');
  126. }
  127. event.deltaY = event.deltaY || 0;
  128. var delta = (event.deltaY > 0 || event.originalEvent.detail < 0 || event.originalEvent.wheelDelta > 0) ? 1 : -1
  129. var scrollEnd = false//have we reached the end of scrolling?
  130. var clientSize = content_wrap[client_size], scrollAmount = content_wrap[scroll_direction];
  131. if( !self.lock ) {
  132. if(delta == -1) scrollEnd = (content_wrap[scroll_size] <= scrollAmount + clientSize);
  133. else scrollEnd = (scrollAmount == 0);
  134. }
  135. self.move_bar(true);
  136. //var step = parseInt( Math.min(Math.max(parseInt(clientSize / 8) , 80) , self.size) ) + 1;
  137. var step = parseInt(clientSize / 8);
  138. if(step < 80) step = 80;
  139. if(step > self.size) step = self.size;
  140. step += 1;
  141. content_wrap[scroll_direction] = scrollAmount - (delta * step);
  142. return scrollEnd && !self.lock_anyway;
  143. })
  144. }
  145. //swipe not available yet
  146. var touchDrag = ace.vars['touch'] && 'ace_drag' in $.event.special && settings.touchDrag //&& !settings.touchSwipe;
  147. //add drag event for touch devices to scroll
  148. if(touchDrag/** || ($.fn.swipe && settings.touchSwipe)*/) {
  149. var dir = '', event_name = touchDrag ? 'ace_drag' : 'swipe';
  150. this.$element.on(event_name + '.ace_scroll', function(event) {
  151. if(disabled) {
  152. event.retval.cancel = true;
  153. return;
  154. }
  155. checkContentChanges(true);
  156. if(!active) {
  157. event.retval.cancel = this.lock_anyway;
  158. return;
  159. }
  160. dir = event.direction;
  161. if( (vertical && (dir == 'up' || dir == 'down'))
  162. ||
  163. (!vertical && (dir == 'left' || dir == 'right'))
  164. )
  165. {
  166. var distance = vertical ? event.dy : event.dx;
  167. if(distance != 0) {
  168. if(Math.abs(distance) > 20 && touchDrag) distance = distance * 2;
  169. self.move_bar(true);
  170. content_wrap[scroll_direction] = content_wrap[scroll_direction] + distance;
  171. }
  172. }
  173. })
  174. }
  175. /////////////////////////////////
  176. if(hideOnIdle) {
  177. $track.addClass('idle-hide');
  178. }
  179. if(observeContent) {
  180. $track.on('mouseenter.ace_scroll', function() {
  181. insideTrack = true;
  182. checkContentChanges(false);
  183. }).on('mouseleave.ace_scroll', function() {
  184. insideTrack = false;
  185. if(mouse_track == false) hideScrollbars();
  186. });
  187. }
  188. //some mobile browsers don't have mouseenter
  189. this.$element.on('mouseenter.ace_scroll touchstart.ace_scroll', function(e) {
  190. //if(ace.vars['old_ie']) return;//IE8 has a problem triggering event two times and strangely wrong values for this.size especially in fullscreen widget!
  191. is_dirty = true;
  192. if(observeContent) checkContentChanges(true);
  193. else if(settings.hoverReset) self.reset(true);
  194. $track.addClass('scroll-hover');
  195. }).on('mouseleave.ace_scroll touchend.ace_scroll', function() {
  196. $track.removeClass('scroll-hover');
  197. });
  198. //
  199. if(!vertical) $content_wrap.children(0).css(css_size, this.size);//the extra wrapper
  200. $content_wrap.css(max_css_size , this.size);
  201. disabled = false;
  202. created = true;
  203. }
  204. this.is_active = function() {
  205. return active;
  206. }
  207. this.is_enabled = function() {
  208. return !disabled;
  209. }
  210. this.move_bar = function($move) {
  211. move_bar = $move;
  212. }
  213. this.get_track = function() {
  214. return track;
  215. }
  216. this.reset = function(innert_call) {
  217. if(disabled) return;// this;
  218. if(!created) this.create();
  219. /////////////////////
  220. var size = this.size;
  221. if(innert_call && !is_dirty) {
  222. return;
  223. }
  224. is_dirty = false;
  225. if(detached) {
  226. var border_size = parseInt(Math.round( (parseInt($content_wrap.css('border-top-width')) + parseInt($content_wrap.css('border-bottom-width'))) / 2.5 ));//(2.5 from trial?!)
  227. size -= border_size;//only if detached
  228. }
  229. var content_size = vertical ? content_wrap[scroll_size] : size;
  230. if( (vertical && content_size == 0) || (!vertical && this.element.scrollWidth == 0) ) {
  231. //element is hidden
  232. //this.$element.addClass('scroll-hidden');
  233. $track.removeClass('scroll-active')
  234. return;// this;
  235. }
  236. var available_space = vertical ? size : content_wrap.clientWidth;
  237. if(!vertical) $content_wrap.children(0).css(css_size, size);//the extra wrapper
  238. $content_wrap.css(max_css_size , this.size);
  239. if(content_size > available_space) {
  240. active = true;
  241. $track.css(css_size, available_space).show();
  242. ratio = parseFloat((available_space / content_size).toFixed(5))
  243. bar_size = parseInt(Math.round(available_space * ratio));
  244. bar_size_2 = parseInt(Math.round(bar_size / 2));
  245. bar_max_pos = available_space - bar_size;
  246. bar_pos = parseInt(Math.round(content_wrap[scroll_direction] * ratio));
  247. bar_style[css_size] = bar_size + 'px';
  248. bar_style[css_pos] = bar_pos + 'px';
  249. $track.addClass('scroll-active');
  250. if(trackSize == 0) {
  251. getTrackSize();
  252. }
  253. if(!reset_once) {
  254. //this.$element.removeClass('scroll-hidden');
  255. if(settings.reset) {
  256. //reset scrollbar to zero position at first
  257. content_wrap[scroll_direction] = 0;
  258. bar_style[css_pos] = 0;
  259. }
  260. reset_once = true;
  261. }
  262. if(detached) setTrackPos();
  263. } else {
  264. active = false;
  265. $track.hide();
  266. $track.removeClass('scroll-active');
  267. $content_wrap.css(max_css_size , '');
  268. }
  269. return;// this;
  270. }
  271. this.disable = function() {
  272. content_wrap[scroll_direction] = 0;
  273. bar_style[css_pos] = 0;
  274. disabled = true;
  275. active = false;
  276. $track.hide();
  277. this.$element.addClass('scroll-disabled');
  278. $track.removeClass('scroll-active');
  279. $content_wrap.css(max_css_size , '');
  280. }
  281. this.enable = function() {
  282. disabled = false;
  283. this.$element.removeClass('scroll-disabled');
  284. }
  285. this.destroy = function() {
  286. active = false;
  287. disabled = false;
  288. created = false;
  289. this.$element.removeClass('ace-scroll scroll-disabled scroll-active');
  290. this.$element.off('.ace_scroll')
  291. if(!detached) {
  292. if(!vertical) {
  293. //remove the extra wrapping div
  294. $content_wrap.find('> div').children().unwrap();
  295. }
  296. $content_wrap.children().unwrap();
  297. $content_wrap.remove();
  298. }
  299. $track.remove();
  300. if(inline_style !== false) this.element.style.position = inline_style;
  301. if(idleTimer != null) {
  302. clearTimeout(idleTimer);
  303. idleTimer = null;
  304. }
  305. }
  306. this.modify = function(_settings) {
  307. if(_settings) settings = $.extend({}, settings, _settings);
  308. this.destroy();
  309. this.create();
  310. is_dirty = true;
  311. this.reset(true);
  312. }
  313. this.update = function(_settings) {
  314. if(_settings) settings = $.extend({}, settings, _settings);
  315. this.size = _settings.size || this.size;
  316. this.lock = _settings.mouseWheelLock || this.lock;
  317. this.lock_anyway = _settings.lockAnyway || this.lock_anyway;
  318. if(_settings.styleClass != undefined) {
  319. if(styleClass) $track.removeClass(styleClass);
  320. styleClass = _settings.styleClass;
  321. if(styleClass) $track.addClass(styleClass);
  322. trackFlip = !!styleClass.match(/scroll\-left|scroll\-top/);
  323. }
  324. }
  325. this.start = function() {
  326. content_wrap[scroll_direction] = 0;
  327. }
  328. this.end = function() {
  329. content_wrap[scroll_direction] = content_wrap[scroll_size];
  330. }
  331. this.hide = function() {
  332. $track.hide();
  333. }
  334. this.show = function() {
  335. $track.show();
  336. }
  337. this.update_scroll = function() {
  338. move_bar = false;
  339. bar_style[css_pos] = bar_pos + 'px';
  340. content_wrap[scroll_direction] = parseInt(Math.round(bar_pos / ratio));
  341. }
  342. function mouse_down_track(e) {
  343. e.preventDefault();
  344. e.stopPropagation();
  345. var track_offset = $track.offset();
  346. var track_pos = track_offset[css_pos];//top for vertical, left for horizontal
  347. var mouse_pos = vertical ? e.pageY : e.pageX;
  348. if(mouse_pos > track_pos + bar_pos) {
  349. bar_pos = mouse_pos - track_pos - bar_size + bar_size_2;
  350. if(bar_pos > bar_max_pos) {
  351. bar_pos = bar_max_pos;
  352. }
  353. }
  354. else {
  355. bar_pos = mouse_pos - track_pos - bar_size_2;
  356. if(bar_pos < 0) bar_pos = 0;
  357. }
  358. self.update_scroll()
  359. }
  360. var mouse_pos1 = -1, mouse_pos2 = -1;
  361. function mouse_down_bar(e) {
  362. e.preventDefault();
  363. e.stopPropagation();
  364. if(vertical) {
  365. mouse_pos2 = mouse_pos1 = e.pageY;
  366. } else {
  367. mouse_pos2 = mouse_pos1 = e.pageX;
  368. }
  369. mouse_track = true;
  370. $('html').off('mousemove.ace_scroll').on('mousemove.ace_scroll', mouse_move_bar)
  371. $(mouse_release_target).off('mouseup.ace_scroll').on('mouseup.ace_scroll', mouse_up_bar);
  372. $track.addClass('active');
  373. if(dragEvent) self.$element.trigger('drag.start');
  374. }
  375. function mouse_move_bar(e) {
  376. e.preventDefault();
  377. e.stopPropagation();
  378. if(vertical) {
  379. mouse_pos2 = e.pageY;
  380. } else {
  381. mouse_pos2 = e.pageX;
  382. }
  383. if(mouse_pos2 - mouse_pos1 + bar_pos > bar_max_pos) {
  384. mouse_pos2 = mouse_pos1 + bar_max_pos - bar_pos;
  385. } else if(mouse_pos2 - mouse_pos1 + bar_pos < 0) {
  386. mouse_pos2 = mouse_pos1 - bar_pos;
  387. }
  388. bar_pos = bar_pos + (mouse_pos2 - mouse_pos1);
  389. mouse_pos1 = mouse_pos2;
  390. if(bar_pos < 0) {
  391. bar_pos = 0;
  392. }
  393. else if(bar_pos > bar_max_pos) {
  394. bar_pos = bar_max_pos;
  395. }
  396. self.update_scroll()
  397. }
  398. function mouse_up_bar(e) {
  399. e.preventDefault();
  400. e.stopPropagation();
  401. mouse_track = false;
  402. $('html').off('.ace_scroll')
  403. $(mouse_release_target).off('.ace_scroll');
  404. $track.removeClass('active');
  405. if(dragEvent) self.$element.trigger('drag.end');
  406. if(active && hideOnIdle && !insideTrack) hideScrollbars();
  407. }
  408. var idleTimer = null;
  409. var prevCheckTime = 0;
  410. function checkContentChanges(hideSoon) {
  411. //check if content size has been modified since last time?
  412. //and with at least 1s delay
  413. var newCheck = +new Date();
  414. if(observeContent && newCheck - prevCheckTime > 1000) {
  415. var newSize = content_wrap[scroll_size];
  416. if(prevContentSize != newSize) {
  417. prevContentSize = newSize;
  418. is_dirty = true;
  419. self.reset(true);
  420. }
  421. prevCheckTime = newCheck;
  422. }
  423. //show scrollbars when not idle anymore i.e. triggered by mousewheel, dragging, etc
  424. if(active && hideOnIdle) {
  425. if(idleTimer != null) {
  426. clearTimeout(idleTimer);
  427. idleTimer = null;
  428. }
  429. $track.addClass('not-idle');
  430. if(!insideTrack && hideSoon == true) {
  431. //hideSoon is false when mouse enters track
  432. hideScrollbars();
  433. }
  434. }
  435. }
  436. function hideScrollbars() {
  437. if(idleTimer != null) {
  438. clearTimeout(idleTimer);
  439. idleTimer = null;
  440. }
  441. idleTimer = setTimeout(function() {
  442. idleTimer = null;
  443. $track.removeClass('not-idle');
  444. } , hideDelay);
  445. }
  446. //for detached scrollbars
  447. function getTrackSize() {
  448. $track.css('visibility', 'hidden').addClass('scroll-hover');
  449. if(vertical) trackSize = parseInt($track.outerWidth()) || 0;
  450. else trackSize = parseInt($track.outerHeight()) || 0;
  451. $track.css('visibility', '').removeClass('scroll-hover');
  452. }
  453. this.track_size = function() {
  454. if(trackSize == 0) getTrackSize();
  455. return trackSize;
  456. }
  457. //for detached scrollbars
  458. function setTrackPos() {
  459. if(updatePos === false) return;
  460. var off = $content_wrap.offset();//because we want it relative to parent not document
  461. var left = off.left;
  462. var top = off.top;
  463. if(vertical) {
  464. if(!trackFlip) {
  465. left += ($content_wrap.outerWidth() - trackSize)
  466. }
  467. }
  468. else {
  469. if(!trackFlip) {
  470. top += ($content_wrap.outerHeight() - trackSize)
  471. }
  472. }
  473. if(updatePos === true) $track.css({top: parseInt(top), left: parseInt(left)});
  474. else if(updatePos === 'left') $track.css('left', parseInt(left));
  475. else if(updatePos === 'top') $track.css('top', parseInt(top));
  476. }
  477. this.create();
  478. is_dirty = true;
  479. this.reset(true);
  480. prevContentSize = content_wrap[scroll_size];
  481. return this;
  482. }
  483. $.fn.ace_scroll = function (option,value) {
  484. var retval;
  485. var $set = this.each(function () {
  486. var $this = $(this);
  487. var data = $this.data('ace_scroll');
  488. var options = typeof option === 'object' && option;
  489. if (!data) $this.data('ace_scroll', (data = new Ace_Scroll(this, options)));
  490. //else if(typeof options == 'object') data['modify'](options);
  491. if (typeof option === 'string') retval = data[option](value);
  492. });
  493. return (retval === undefined) ? $set : retval;
  494. };
  495. $.fn.ace_scroll.defaults = {
  496. 'size' : 200,
  497. 'horizontal': false,
  498. 'mouseWheel': true,
  499. 'mouseWheelLock': false,
  500. 'lockAnyway': false,
  501. 'styleClass' : false,
  502. 'observeContent': false,
  503. 'hideOnIdle': false,
  504. 'hideDelay': 1500,
  505. 'hoverReset': true //reset scrollbar sizes on mouse hover because of possible sizing changes
  506. ,
  507. 'reset': false //true= set scrollTop = 0
  508. ,
  509. 'dragEvent': false
  510. ,
  511. 'touchDrag': true
  512. ,
  513. 'touchSwipe': false
  514. ,
  515. 'scrollEvent': false //trigger scroll event
  516. ,
  517. 'detached': false
  518. ,
  519. 'updatePos': true
  520. /**
  521. ,
  522. 'track' : true,
  523. 'show' : false,
  524. 'dark': false,
  525. 'alwaysVisible': false,
  526. 'margin': false,
  527. 'thin': false,
  528. 'position': 'right'
  529. */
  530. }
  531. /**
  532. $(document).on('ace.settings.ace_scroll', function(e, name) {
  533. if(name == 'sidebar_collapsed') $('.ace-scroll').scroller('reset');
  534. });
  535. $(window).on('resize.ace_scroll', function() {
  536. $('.ace-scroll').scroller('reset');
  537. });
  538. */
  539. })(window.jQuery);;/**
  540. <b>Custom color picker element</b>. Converts html select elements to a dropdown color picker.
  541. */
  542. (function($ , undefined) {
  543. var Ace_Colorpicker = function(element, option) {
  544. var options = $.extend({}, $.fn.ace_colorpicker.defaults, option);
  545. var $element = $(element);
  546. var color_list = '';
  547. var color_selected = '';
  548. var selection = null;
  549. var color_array = [];
  550. $element.addClass('hide').find('option').each(function() {
  551. var $class = 'colorpick-btn';
  552. var color = this.value.replace(/[^\w\s,#\(\)\.]/g, '');
  553. if(this.value != color) this.value = color;
  554. if(this.selected) {
  555. $class += ' selected';
  556. color_selected = color;
  557. }
  558. color_array.push(color)
  559. color_list += '<li><a class="'+$class+'" href="#" style="background-color:'+color+';" data-color="'+color+'"></a></li>';
  560. }).
  561. end()
  562. .on('change.color', function(){
  563. $element.next().find('.btn-colorpicker').css('background-color', this.value);
  564. })
  565. .after('<div class="dropdown dropdown-colorpicker">\
  566. <a data-toggle="dropdown" class="dropdown-toggle" '+(options.auto_pos ? 'data-position="auto"' : '')+' href="#"><span class="btn-colorpicker" style="background-color:'+color_selected+'"></span></a><ul class="dropdown-menu'+(options.caret? ' dropdown-caret' : '')+(options.pull_right ? ' dropdown-menu-right' : '')+'">'+color_list+'</ul></div>')
  567. var dropdown = $element.next().find('.dropdown-menu')
  568. dropdown.on(ace.click_event, function(e) {
  569. var a = $(e.target);
  570. if(!a.is('.colorpick-btn')) return false;
  571. if(selection) selection.removeClass('selected');
  572. selection = a;
  573. selection.addClass('selected');
  574. var color = selection.data('color');
  575. $element.val(color).trigger('change');
  576. e.preventDefault();
  577. return true;//to hide dropdown
  578. })
  579. selection = $element.next().find('a.selected');
  580. this.pick = function(index, insert) {
  581. if(typeof index === 'number') {
  582. if(index >= color_array.length) return;
  583. element.selectedIndex = index;
  584. dropdown.find('a:eq('+index+')').trigger(ace.click_event);
  585. }
  586. else if(typeof index === 'string') {
  587. var color = index.replace(/[^\w\s,#\(\)\.]/g, '');
  588. index = color_array.indexOf(color);
  589. //add this color if it doesn't exist
  590. if(index == -1 && insert === true) {
  591. color_array.push(color);
  592. $('<option />')
  593. .appendTo($element)
  594. .val(color);
  595. $('<li><a class="colorpick-btn" href="#"></a></li>')
  596. .appendTo(dropdown)
  597. .find('a')
  598. .css('background-color', color)
  599. .data('color', color);
  600. index = color_array.length - 1;
  601. }
  602. if(index == -1) return;
  603. dropdown.find('a:eq('+index+')').trigger(ace.click_event);
  604. }
  605. }
  606. this.destroy = function() {
  607. $element.removeClass('hide').off('change.color')
  608. .next().remove();
  609. color_array = [];
  610. }
  611. }
  612. $.fn.ace_colorpicker = function(option, value) {
  613. var retval;
  614. var $set = this.each(function () {
  615. var $this = $(this);
  616. var data = $this.data('ace_colorpicker');
  617. var options = typeof option === 'object' && option;
  618. if (!data) $this.data('ace_colorpicker', (data = new Ace_Colorpicker(this, options)));
  619. if (typeof option === 'string') retval = data[option](value);
  620. });
  621. return (retval === undefined) ? $set : retval;
  622. }
  623. $.fn.ace_colorpicker.defaults = {
  624. 'pull_right' : false,
  625. 'caret': true,
  626. 'auto_pos': true
  627. }
  628. })(window.jQuery);;/**
  629. <b>Ace file input element</b>. Custom, simple file input element to style browser's default file input.
  630. */
  631. (function($ , undefined) {
  632. var multiplible = 'multiple' in document.createElement('INPUT');
  633. var hasFileList = 'FileList' in window;//file list enabled in modern browsers
  634. var hasFileReader = 'FileReader' in window;
  635. var hasFile = 'File' in window;
  636. var Ace_File_Input = function(element , settings) {
  637. var self = this;
  638. this.settings = $.extend({}, $.fn.ace_file_input.defaults, settings);
  639. this.$element = $(element);
  640. this.element = element;
  641. this.disabled = false;
  642. this.can_reset = true;
  643. this.$element
  644. .off('change.ace_inner_call')
  645. .on('change.ace_inner_call', function(e , ace_inner_call){
  646. if(self.disabled) return;
  647. if(ace_inner_call === true) return;//this change event is called from above drop event and extra checkings are taken care of there
  648. return handle_on_change.call(self);
  649. //if(ret === false) e.preventDefault();
  650. });
  651. var parent_label = this.$element.closest('label').css({'display':'block'})
  652. var tagName = parent_label.length == 0 ? 'label' : 'span';//if not inside a "LABEL" tag, use "LABEL" tag, otherwise use "SPAN"
  653. this.$element.wrap('<'+tagName+' class="ace-file-input" />');
  654. this.apply_settings();
  655. this.reset_input_field();//for firefox as it keeps selected file after refresh
  656. }
  657. Ace_File_Input.error = {
  658. 'FILE_LOAD_FAILED' : 1,
  659. 'IMAGE_LOAD_FAILED' : 2,
  660. 'THUMBNAIL_FAILED' : 3
  661. };
  662. Ace_File_Input.prototype.apply_settings = function() {
  663. var self = this;
  664. this.multi = this.$element.attr('multiple') && multiplible;
  665. this.well_style = this.settings.style == 'well';
  666. if(this.well_style) this.$element.parent().addClass('ace-file-multiple');
  667. else this.$element.parent().removeClass('ace-file-multiple');
  668. this.$element.parent().find(':not(input[type=file])').remove();//remove all except our input, good for when changing settings
  669. this.$element.after('<span class="ace-file-container" data-title="'+this.settings.btn_choose+'"><span class="ace-file-name" data-title="'+this.settings.no_file+'">'+(this.settings.no_icon ? '<i class="'+ ace.vars['icon'] + this.settings.no_icon+'"></i>' : '')+'</span></span>');
  670. this.$label = this.$element.next();
  671. this.$container = this.$element.closest('.ace-file-input');
  672. var remove_btn = !!this.settings.icon_remove;
  673. if(remove_btn) {
  674. var btn =
  675. $('<a class="remove" href="#"><i class="'+ ace.vars['icon'] + this.settings.icon_remove+'"></i></a>')
  676. .appendTo(this.$element.parent());
  677. btn.on(ace.click_event, function(e){
  678. e.preventDefault();
  679. if( !self.can_reset ) return false;
  680. var ret = true;
  681. if(self.settings.before_remove) ret = self.settings.before_remove.call(self.element);
  682. if(!ret) return false;
  683. var r = self.reset_input();
  684. return false;
  685. });
  686. }
  687. if(this.settings.droppable && hasFileList) {
  688. enable_drop_functionality.call(this);
  689. }
  690. }
  691. Ace_File_Input.prototype.show_file_list = function($files , inner_call) {
  692. var files = typeof $files === "undefined" ? this.$element.data('ace_input_files') : $files;
  693. if(!files || files.length == 0) return;
  694. //////////////////////////////////////////////////////////////////
  695. if(this.well_style) {
  696. this.$label.find('.ace-file-name').remove();
  697. if(!this.settings.btn_change) this.$label.addClass('hide-placeholder');
  698. }
  699. this.$label.attr('data-title', this.settings.btn_change).addClass('selected');
  700. for (var i = 0; i < files.length; i++) {
  701. var filename = '', format = false;
  702. if(typeof files[i] === "string") filename = files[i];
  703. else if(hasFile && files[i] instanceof File) filename = $.trim( files[i].name );
  704. else if(files[i] instanceof Object && files[i].hasOwnProperty('name')) {
  705. //format & name specified by user (pre-displaying name, etc)
  706. filename = files[i].name;
  707. if(files[i].hasOwnProperty('type')) format = files[i].type;
  708. if(!files[i].hasOwnProperty('path')) files[i].path = files[i].name;
  709. }
  710. else continue;
  711. var index = filename.lastIndexOf("\\") + 1;
  712. if(index == 0)index = filename.lastIndexOf("/") + 1;
  713. filename = filename.substr(index);
  714. if(format == false) {
  715. if((/\.(jpe?g|png|gif|svg|bmp|tiff?)$/i).test(filename)) {
  716. format = 'image';
  717. }
  718. else if((/\.(mpe?g|flv|mov|avi|swf|mp4|mkv|webm|wmv|3gp)$/i).test(filename)) {
  719. format = 'video';
  720. }
  721. else if((/\.(mp3|ogg|wav|wma|amr|aac)$/i).test(filename)) {
  722. format = 'audio';
  723. }
  724. else format = 'file';
  725. }
  726. var fileIcons = {
  727. 'file' : 'fa fa-file',
  728. 'image' : 'fa fa-picture-o file-image',
  729. 'video' : 'fa fa-film file-video',
  730. 'audio' : 'fa fa-music file-audio'
  731. };
  732. var fileIcon = fileIcons[format];
  733. if(!this.well_style) this.$label.find('.ace-file-name').attr({'data-title':filename}).find(ace.vars['.icon']).attr('class', ace.vars['icon'] + fileIcon);
  734. else {
  735. this.$label.append('<span class="ace-file-name" data-title="'+filename+'"><i class="'+ ace.vars['icon'] + fileIcon+'"></i></span>');
  736. var type = (inner_call === true && hasFile && files[i] instanceof File) ? $.trim(files[i].type) : '';
  737. var can_preview = hasFileReader && this.settings.thumbnail
  738. &&
  739. ( (type.length > 0 && type.match('image')) || (type.length == 0 && format == 'image') )//the second one is for older Android's default browser which gives an empty text for file.type
  740. if(can_preview) {
  741. var self = this;
  742. $.when(preview_image.call(this, files[i])).fail(function(result){
  743. //called on failure to load preview
  744. if(self.settings.preview_error) self.settings.preview_error.call(self, filename, result.code);
  745. })
  746. }
  747. }
  748. }
  749. return true;
  750. }
  751. Ace_File_Input.prototype.reset_input = function() {
  752. this.reset_input_ui();
  753. this.reset_input_field();
  754. }
  755. Ace_File_Input.prototype.reset_input_ui = function() {
  756. this.$label.attr({'data-title':this.settings.btn_choose, 'class':'ace-file-container'})
  757. .find('.ace-file-name:first').attr({'data-title':this.settings.no_file , 'class':'ace-file-name'})
  758. .find(ace.vars['.icon']).attr('class', ace.vars['icon'] + this.settings.no_icon)
  759. .prev('img').remove();
  760. if(!this.settings.no_icon) this.$label.find(ace.vars['.icon']).remove();
  761. this.$label.find('.ace-file-name').not(':first').remove();
  762. this.reset_input_data();
  763. //if(ace.vars['old_ie']) ace.helper.redraw(this.$container[0]);
  764. }
  765. Ace_File_Input.prototype.reset_input_field = function() {
  766. //http://stackoverflow.com/questions/1043957/clearing-input-type-file-using-jquery/13351234#13351234
  767. this.$element.wrap('<form>').parent().get(0).reset();
  768. this.$element.unwrap();
  769. //strangely when reset is called on this temporary inner form
  770. //only **IE9/10** trigger 'reset' on the outer form as well
  771. //and as we have mentioned to reset input on outer form reset
  772. //it causes infinite recusrsion by coming back to reset_input_field
  773. //thus calling reset again and again and again
  774. //so because when "reset" button of outer form is hit, file input is automatically reset
  775. //we just reset_input_ui to avoid recursion
  776. }
  777. Ace_File_Input.prototype.reset_input_data = function() {
  778. if(this.$element.data('ace_input_files')) {
  779. this.$element.removeData('ace_input_files');
  780. this.$element.removeData('ace_input_method');
  781. }
  782. }
  783. Ace_File_Input.prototype.enable_reset = function(can_reset) {
  784. this.can_reset = can_reset;
  785. }
  786. Ace_File_Input.prototype.disable = function() {
  787. this.disabled = true;
  788. this.$element.attr('disabled', 'disabled').addClass('disabled');
  789. }
  790. Ace_File_Input.prototype.enable = function() {
  791. this.disabled = false;
  792. this.$element.removeAttr('disabled').removeClass('disabled');
  793. }
  794. Ace_File_Input.prototype.files = function() {
  795. return $(this).data('ace_input_files') || null;
  796. }
  797. Ace_File_Input.prototype.method = function() {
  798. return $(this).data('ace_input_method') || '';
  799. }
  800. Ace_File_Input.prototype.update_settings = function(new_settings) {
  801. this.settings = $.extend({}, this.settings, new_settings);
  802. this.apply_settings();
  803. }
  804. Ace_File_Input.prototype.loading = function(is_loading) {
  805. if(is_loading === false) {
  806. this.$container.find('.ace-file-overlay').remove();
  807. this.element.removeAttribute('readonly');
  808. }
  809. else {
  810. var inside = typeof is_loading === 'string' ? is_loading : '<i class="overlay-content fa fa-spin fa-spinner orange2 fa-2x"></i>';
  811. var loader = this.$container.find('.ace-file-overlay');
  812. if(loader.length == 0) {
  813. loader = $('<div class="ace-file-overlay"></div>').appendTo(this.$container);
  814. loader.on('click tap', function(e) {
  815. e.stopImmediatePropagation();
  816. e.preventDefault();
  817. return false;
  818. });
  819. this.element.setAttribute('readonly' , 'true');//for IE
  820. }
  821. loader.empty().append(inside);
  822. }
  823. }
  824. var enable_drop_functionality = function() {
  825. var self = this;
  826. var dropbox = this.$element.parent();
  827. dropbox
  828. .off('dragenter')
  829. .on('dragenter', function(e){
  830. e.preventDefault();
  831. e.stopPropagation();
  832. })
  833. .off('dragover')
  834. .on('dragover', function(e){
  835. e.preventDefault();
  836. e.stopPropagation();
  837. })
  838. .off('drop')
  839. .on('drop', function(e){
  840. e.preventDefault();
  841. e.stopPropagation();
  842. if(self.disabled) return;
  843. var dt = e.originalEvent.dataTransfer;
  844. var file_list = dt.files;
  845. if(!self.multi && file_list.length > 1) {//single file upload, but dragged multiple files
  846. var tmpfiles = [];
  847. tmpfiles.push(file_list[0]);
  848. file_list = tmpfiles;//keep only first file
  849. }
  850. file_list = processFiles.call(self, file_list, true);//true means files have been selected, not dropped
  851. if(file_list === false) return false;
  852. self.$element.data('ace_input_method', 'drop');
  853. self.$element.data('ace_input_files', file_list);//save files data to be used later by user
  854. self.show_file_list(file_list , true);
  855. self.$element.triggerHandler('change' , [true]);//true means ace_inner_call
  856. return true;
  857. });
  858. }
  859. var handle_on_change = function() {
  860. var file_list = this.element.files || [this.element.value];/** make it an array */
  861. file_list = processFiles.call(this, file_list, false);//false means files have been selected, not dropped
  862. if(file_list === false) return false;
  863. this.$element.data('ace_input_method', 'select');
  864. this.$element.data('ace_input_files', file_list);
  865. this.show_file_list(file_list , true);
  866. return true;
  867. }
  868. var preview_image = function(file) {
  869. var self = this;
  870. var $span = self.$label.find('.ace-file-name:last');//it should be out of onload, otherwise all onloads may target the same span because of delays
  871. var deferred = new $.Deferred;
  872. var getImage = function(src) {
  873. $span.prepend("<img class='middle' style='display:none;' />");
  874. var img = $span.find('img:last').get(0);
  875. $(img).one('load', function() {
  876. imgLoaded.call(null, img);
  877. }).one('error', function() {
  878. imgFailed.call(null, img);
  879. });
  880. img.src = src;
  881. }
  882. var imgLoaded = function(img) {
  883. //if image loaded successfully
  884. var size = 50;
  885. if(self.settings.thumbnail == 'large') size = 150;
  886. else if(self.settings.thumbnail == 'fit') size = $span.width();
  887. $span.addClass(size > 50 ? 'large' : '');
  888. var thumb = get_thumbnail(img, size/**, file.type*/);
  889. if(thumb == null) {
  890. //if making thumbnail fails
  891. $(this).remove();
  892. deferred.reject({code:Ace_File_Input.error['THUMBNAIL_FAILED']});
  893. return;
  894. }
  895. var w = thumb.w, h = thumb.h;
  896. if(self.settings.thumbnail == 'small') {w=h=size;};
  897. $(img).css({'background-image':'url('+thumb.src+')' , width:w, height:h})
  898. .data('thumb', thumb.src)
  899. .attr({src:'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg=='})
  900. .show()
  901. ///////////////////
  902. deferred.resolve();
  903. }
  904. var imgFailed = function(img) {
  905. //for example when a file has image extenstion, but format is something else
  906. $span.find('img').remove();
  907. deferred.reject({code:Ace_File_Input.error['IMAGE_LOAD_FAILED']});
  908. }
  909. if(hasFile && file instanceof File) {
  910. var reader = new FileReader();
  911. reader.onload = function (e) {
  912. getImage(e.target.result);
  913. }
  914. reader.onerror = function (e) {
  915. deferred.reject({code:Ace_File_Input.error['FILE_LOAD_FAILED']});
  916. }
  917. reader.readAsDataURL(file);
  918. }
  919. else {
  920. if(file instanceof Object && file.hasOwnProperty('path')) {
  921. getImage(file.path);//file is a file name (path) --- this is used to pre-show user-selected image
  922. }
  923. }
  924. return deferred.promise();
  925. }
  926. var get_thumbnail = function(img, size, type) {
  927. var w = img.width, h = img.height;
  928. //**IE10** is not giving correct width using img.width so we use $(img).width()
  929. w = w > 0 ? w : $(img).width()
  930. h = h > 0 ? h : $(img).height()
  931. if(w > size || h > size) {
  932. if(w > h) {
  933. h = parseInt(size/w * h);
  934. w = size;
  935. } else {
  936. w = parseInt(size/h * w);
  937. h = size;
  938. }
  939. }
  940. var dataURL
  941. try {
  942. var canvas = document.createElement('canvas');
  943. canvas.width = w; canvas.height = h;
  944. var context = canvas.getContext('2d');
  945. context.drawImage(img, 0, 0, img.width, img.height, 0, 0, w, h);
  946. dataURL = canvas.toDataURL(/*type == 'image/jpeg' ? type : 'image/png', 10*/)
  947. } catch(e) {
  948. dataURL = null;
  949. }
  950. if(! dataURL) return null;
  951. //there was only one image that failed in firefox completely randomly! so let's double check things
  952. if( !( /^data\:image\/(png|jpe?g|gif);base64,[0-9A-Za-z\+\/\=]+$/.test(dataURL)) ) dataURL = null;
  953. if(! dataURL) return null;
  954. return {src: dataURL, w:w, h:h};
  955. }
  956. var processFiles = function(file_list, dropped) {
  957. var ret = checkFileList.call(this, file_list, dropped);
  958. if(ret === -1) {
  959. this.reset_input();
  960. return false;
  961. }
  962. if( !ret || ret.length == 0 ) {
  963. if( !this.$element.data('ace_input_files') ) this.reset_input();
  964. //if nothing selected before, reset because of the newly unacceptable (ret=false||length=0) selection
  965. //otherwise leave the previous selection intact?!!!
  966. return false;
  967. }
  968. if (ret instanceof Array || (hasFileList && ret instanceof FileList)) file_list = ret;
  969. ret = true;
  970. if(this.settings.before_change) ret = this.settings.before_change.call(this.element, file_list, dropped);
  971. if(ret === -1) {
  972. this.reset_input();
  973. return false;
  974. }
  975. if(!ret || ret.length == 0) {
  976. if( !this.$element.data('ace_input_files') ) this.reset_input();
  977. return false;
  978. }
  979. //inside before_change you can return a modified File Array as result
  980. if (ret instanceof Array || (hasFileList && ret instanceof FileList)) file_list = ret;
  981. return file_list;
  982. }
  983. var getExtRegex = function(ext) {
  984. if(!ext) return null;
  985. if(typeof ext === 'string') ext = [ext];
  986. if(ext.length == 0) return null;
  987. return new RegExp("\.(?:"+ext.join('|')+")$", "i");
  988. }
  989. var getMimeRegex = function(mime) {
  990. if(!mime) return null;
  991. if(typeof mime === 'string') mime = [mime];
  992. if(mime.length == 0) return null;
  993. return new RegExp("^(?:"+mime.join('|').replace(/\//g, "\\/")+")$", "i");
  994. }
  995. var checkFileList = function(files, dropped) {
  996. var allowExt = getExtRegex(this.settings.allowExt);
  997. var denyExt = getExtRegex(this.settings.denyExt);
  998. var allowMime = getMimeRegex(this.settings.allowMime);
  999. var denyMime = getMimeRegex(this.settings.denyMime);
  1000. var maxSize = this.settings.maxSize || false;
  1001. if( !(allowExt || denyExt || allowMime || denyMime || maxSize) ) return true;//no checking required
  1002. var safe_files = [];
  1003. var error_list = {}
  1004. for(var f = 0; f < files.length; f++) {
  1005. var file = files[f];
  1006. //file is either a string(file name) or a File object
  1007. var filename = !hasFile ? file : file.name;
  1008. if( allowExt && !allowExt.test(filename) ) {
  1009. //extension not matching whitelist, so drop it
  1010. if(!('ext' in error_list)) error_list['ext'] = [];
  1011. error_list['ext'].push(filename);
  1012. continue;
  1013. } else if( denyExt && denyExt.test(filename) ) {
  1014. //extension is matching blacklist, so drop it
  1015. if(!('ext' in error_list)) error_list['ext'] = [];
  1016. error_list['ext'].push(filename);
  1017. continue;
  1018. }
  1019. var type;
  1020. if( !hasFile ) {
  1021. //in browsers that don't support FileReader API
  1022. safe_files.push(file);
  1023. continue;
  1024. }
  1025. else if((type = $.trim(file.type)).length > 0) {
  1026. //there is a mimetype for file so let's check against are rules
  1027. if( allowMime && !allowMime.test(type) ) {
  1028. //mimeType is not matching whitelist, so drop it
  1029. if(!('mime' in error_list)) error_list['mime'] = [];
  1030. error_list['mime'].push(filename);
  1031. continue;
  1032. }
  1033. else if( denyMime && denyMime.test(type) ) {
  1034. //mimeType is matching blacklist, so drop it
  1035. if(!('mime' in error_list)) error_list['mime'] = [];
  1036. error_list['mime'].push(filename);
  1037. continue;
  1038. }
  1039. }
  1040. if( maxSize && file.size > maxSize ) {
  1041. //file size is not acceptable
  1042. if(!('size' in error_list)) error_list['size'] = [];
  1043. error_list['size'].push(filename);
  1044. continue;
  1045. }
  1046. safe_files.push(file)
  1047. }
  1048. if(safe_files.length == files.length) return files;//return original file list if all are valid
  1049. /////////
  1050. var error_count = {'ext': 0, 'mime': 0, 'size': 0}
  1051. if( 'ext' in error_list ) error_count['ext'] = error_list['ext'].length;
  1052. if( 'mime' in error_list ) error_count['mime'] = error_list['mime'].length;
  1053. if( 'size' in error_list ) error_count['size'] = error_list['size'].length;
  1054. var event
  1055. this.$element.trigger(
  1056. event = new $.Event('file.error.ace'),
  1057. {
  1058. 'file_count': files.length,
  1059. 'invalid_count' : files.length - safe_files.length,
  1060. 'error_list' : error_list,
  1061. 'error_count' : error_count,
  1062. 'dropped': dropped
  1063. }
  1064. );
  1065. if ( event.isDefaultPrevented() ) return -1;//it will reset input
  1066. //////////
  1067. return safe_files;//return safe_files
  1068. }
  1069. ///////////////////////////////////////////
  1070. $.fn.aceFileInput = $.fn.ace_file_input = function (option,value) {
  1071. var retval;
  1072. var $set = this.each(function () {
  1073. var $this = $(this);
  1074. var data = $this.data('ace_file_input');
  1075. var options = typeof option === 'object' && option;
  1076. if (!data) $this.data('ace_file_input', (data = new Ace_File_Input(this, options)));
  1077. if (typeof option === 'string') retval = data[option](value);
  1078. });
  1079. return (retval === undefined) ? $set : retval;
  1080. };
  1081. $.fn.aceFileInput.defaults = $.fn.ace_file_input.defaults = {
  1082. style: false,
  1083. no_file: 'No File ...',
  1084. no_icon: 'fa fa-upload',
  1085. btn_choose: 'Choose',
  1086. btn_change: 'Change',
  1087. icon_remove: 'fa fa-times',
  1088. droppable: false,
  1089. thumbnail: false,//large, fit, small
  1090. allowExt: null,
  1091. denyExt: null,
  1092. allowMime: null,
  1093. denyMime: null,
  1094. maxSize: false,
  1095. //callbacks
  1096. before_change: null,
  1097. before_remove: null,
  1098. preview_error: null
  1099. }
  1100. })(window.jQuery);
  1101. ;/**
  1102. <b>Bootstrap 2 typeahead plugin.</b> With Bootstrap <u>3</u> it's been dropped in favor of a more advanced separate plugin.
  1103. Pretty good for simple cases such as autocomplete feature of the search box and required for <u class="text-danger">Tag input</u> plugin.
  1104. */
  1105. /* =============================================================
  1106. * bootstrap-typeahead.js v2.3.2
  1107. * http://twitter.github.com/bootstrap/javascript.html#typeahead
  1108. * =============================================================
  1109. * Copyright 2012 Twitter, Inc.
  1110. *
  1111. * Licensed under the Apache License, Version 2.0 (the "License");
  1112. * you may not use this file except in compliance with the License.
  1113. * You may obtain a copy of the License at
  1114. *
  1115. * http://www.apache.org/licenses/LICENSE-2.0
  1116. *
  1117. * Unless required by applicable law or agreed to in writing, software
  1118. * distributed under the License is distributed on an "AS IS" BASIS,
  1119. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  1120. * See the License for the specific language governing permissions and
  1121. * limitations under the License.
  1122. * ============================================================ */
  1123. !function($){
  1124. "use strict"; // jshint ;_;
  1125. /* TYPEAHEAD PUBLIC CLASS DEFINITION
  1126. * ================================= */
  1127. var Typeahead = function (element, options) {
  1128. this.$element = $(element)
  1129. this.options = $.extend({}, $.fn.bs_typeahead.defaults, options)
  1130. this.matcher = this.options.matcher || this.matcher
  1131. this.sorter = this.options.sorter || this.sorter
  1132. this.highlighter = this.options.highlighter || this.highlighter
  1133. this.updater = this.options.updater || this.updater
  1134. this.source = this.options.source
  1135. this.$menu = $(this.options.menu)
  1136. this.shown = false
  1137. this.listen()
  1138. }
  1139. Typeahead.prototype = {
  1140. constructor: Typeahead
  1141. , select: function () {
  1142. var val = this.$menu.find('.active').attr('data-value')
  1143. this.$element
  1144. .val(this.updater(val))
  1145. .change()
  1146. return this.hide()
  1147. }
  1148. , updater: function (item) {
  1149. return item
  1150. }
  1151. , show: function () {
  1152. var pos = $.extend({}, this.$element.position(), {
  1153. height: this.$element[0].offsetHeight
  1154. })
  1155. this.$menu
  1156. .insertAfter(this.$element)
  1157. .css({
  1158. top: pos.top + pos.height
  1159. , left: pos.left
  1160. })
  1161. .show()
  1162. this.shown = true
  1163. return this
  1164. }
  1165. , hide: function () {
  1166. this.$menu.hide()
  1167. this.shown = false
  1168. return this
  1169. }
  1170. , lookup: function (event) {
  1171. var items
  1172. this.query = this.$element.val()
  1173. if (!this.query || this.query.length < this.options.minLength) {
  1174. return this.shown ? this.hide() : this
  1175. }
  1176. items = $.isFunction(this.source) ? this.source(this.query, $.proxy(this.process, this)) : this.source
  1177. return items ? this.process(items) : this
  1178. }
  1179. , process: function (items) {
  1180. var that = this
  1181. items = $.grep(items, function (item) {
  1182. return that.matcher(item)
  1183. })
  1184. items = this.sorter(items)
  1185. if (!items.length) {
  1186. return this.shown ? this.hide() : this
  1187. }
  1188. return this.render(items.slice(0, this.options.items)).show()
  1189. }
  1190. , matcher: function (item) {
  1191. return ~item.toLowerCase().indexOf(this.query.toLowerCase())
  1192. }
  1193. , sorter: function (items) {
  1194. var beginswith = []
  1195. , caseSensitive = []
  1196. , caseInsensitive = []
  1197. , item
  1198. while (item = items.shift()) {
  1199. if (!item.toLowerCase().indexOf(this.query.toLowerCase())) beginswith.push(item)
  1200. else if (~item.indexOf(this.query)) caseSensitive.push(item)
  1201. else caseInsensitive.push(item)
  1202. }
  1203. return beginswith.concat(caseSensitive, caseInsensitive)
  1204. }
  1205. , highlighter: function (item) {
  1206. var query = this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&')
  1207. return item.replace(new RegExp('(' + query + ')', 'ig'), function ($1, match) {
  1208. return '<strong>' + match + '</strong>'
  1209. })
  1210. }
  1211. , render: function (items) {
  1212. var that = this
  1213. items = $(items).map(function (i, item) {
  1214. i = $(that.options.item).attr('data-value', item)
  1215. i.find('a').html(that.highlighter(item))
  1216. return i[0]
  1217. })
  1218. items.first().addClass('active')
  1219. this.$menu.html(items)
  1220. return this
  1221. }
  1222. , next: function (event) {
  1223. var active = this.$menu.find('.active').removeClass('active')
  1224. , next = active.next()
  1225. if (!next.length) {
  1226. next = $(this.$menu.find('li')[0])
  1227. }
  1228. next.addClass('active')
  1229. }
  1230. , prev: function (event) {
  1231. var active = this.$menu.find('.active').removeClass('active')
  1232. , prev = active.prev()
  1233. if (!prev.length) {
  1234. prev = this.$menu.find('li').last()
  1235. }
  1236. prev.addClass('active')
  1237. }
  1238. , listen: function () {
  1239. this.$element
  1240. .on('focus', $.proxy(this.focus, this))
  1241. .on('blur', $.proxy(this.blur, this))
  1242. .on('keypress', $.proxy(this.keypress, this))
  1243. .on('keyup', $.proxy(this.keyup, this))
  1244. if (this.eventSupported('keydown')) {
  1245. this.$element.on('keydown', $.proxy(this.keydown, this))
  1246. }
  1247. this.$menu
  1248. .on('click', $.proxy(this.click, this))
  1249. .on('mouseenter', 'li', $.proxy(this.mouseenter, this))
  1250. .on('mouseleave', 'li', $.proxy(this.mouseleave, this))
  1251. }
  1252. , eventSupported: function(eventName) {
  1253. var isSupported = eventName in this.$element
  1254. if (!isSupported) {
  1255. this.$element.setAttribute(eventName, 'return;')
  1256. isSupported = typeof this.$element[eventName] === 'function'
  1257. }
  1258. return isSupported
  1259. }
  1260. , move: function (e) {
  1261. if (!this.shown) return
  1262. switch(e.keyCode) {
  1263. case 9: // tab
  1264. case 13: // enter
  1265. case 27: // escape
  1266. e.preventDefault()
  1267. break
  1268. case 38: // up arrow
  1269. e.preventDefault()
  1270. this.prev()
  1271. break
  1272. case 40: // down arrow
  1273. e.preventDefault()
  1274. this.next()
  1275. break
  1276. }
  1277. e.stopPropagation()
  1278. }
  1279. , keydown: function (e) {
  1280. this.suppressKeyPressRepeat = ~$.inArray(e.keyCode, [40,38,9,13,27])
  1281. this.move(e)
  1282. }
  1283. , keypress: function (e) {
  1284. if (this.suppressKeyPressRepeat) return
  1285. this.move(e)
  1286. }
  1287. , keyup: function (e) {
  1288. switch(e.keyCode) {
  1289. case 40: // down arrow
  1290. case 38: // up arrow
  1291. case 16: // shift
  1292. case 17: // ctrl
  1293. case 18: // alt
  1294. break
  1295. case 9: // tab
  1296. case 13: // enter
  1297. if (!this.shown) return
  1298. this.select()
  1299. break
  1300. case 27: // escape
  1301. if (!this.shown) return
  1302. this.hide()
  1303. break
  1304. default:
  1305. this.lookup()
  1306. }
  1307. e.stopPropagation()
  1308. e.preventDefault()
  1309. }
  1310. , focus: function (e) {
  1311. this.focused = true
  1312. }
  1313. , blur: function (e) {
  1314. this.focused = false
  1315. if (!this.mousedover && this.shown) this.hide()
  1316. }
  1317. , click: function (e) {
  1318. e.stopPropagation()
  1319. e.preventDefault()
  1320. this.select()
  1321. this.$element.focus()
  1322. }
  1323. , mouseenter: function (e) {
  1324. this.mousedover = true
  1325. this.$menu.find('.active').removeClass('active')
  1326. $(e.currentTarget).addClass('active')
  1327. }
  1328. , mouseleave: function (e) {
  1329. this.mousedover = false
  1330. if (!this.focused && this.shown) this.hide()
  1331. }
  1332. }
  1333. /* TYPEAHEAD PLUGIN DEFINITION
  1334. * =========================== */
  1335. var old = $.fn.bs_typeahead
  1336. $.fn.bs_typeahead = function (option) {
  1337. return this.each(function () {
  1338. var $this = $(this)
  1339. , data = $this.data('bs_typeahead')
  1340. , options = typeof option == 'object' && option
  1341. if (!data) $this.data('bs_typeahead', (data = new Typeahead(this, options)))
  1342. if (typeof option == 'string') data[option]()
  1343. })
  1344. }
  1345. $.fn.bs_typeahead.defaults = {
  1346. source: []
  1347. , items: 8
  1348. , menu: '<ul class="typeahead dropdown-menu"></ul>'
  1349. , item: '<li><a href="#"></a></li>'
  1350. , minLength: 1
  1351. }
  1352. $.fn.bs_typeahead.Constructor = Typeahead
  1353. /* TYPEAHEAD NO CONFLICT
  1354. * =================== */
  1355. $.fn.bs_typeahead.noConflict = function () {
  1356. $.fn.bs_typeahead = old
  1357. return this
  1358. }
  1359. /* TYPEAHEAD DATA-API
  1360. * ================== */
  1361. $(document).on('focus.bs_typeahead.data-api', '[data-provide="bs_typeahead"]', function (e) {
  1362. var $this = $(this)
  1363. if ($this.data('bs_typeahead')) return
  1364. $this.bs_typeahead($this.data())
  1365. })
  1366. }(window.jQuery);;/**
  1367. <b>Wysiwyg</b>. A wrapper for Bootstrap wyswiwyg plugin.
  1368. It's just a wrapper so you still need to include Bootstrap wysiwyg script first.
  1369. */
  1370. (function($ , undefined) {
  1371. $.fn.ace_wysiwyg = function($options , undefined) {
  1372. var options = $.extend( {
  1373. speech_button:true,
  1374. wysiwyg:{}
  1375. }, $options);
  1376. var color_values = [
  1377. '#ac725e','#d06b64','#f83a22','#fa573c','#ff7537','#ffad46',
  1378. '#42d692','#16a765','#7bd148','#b3dc6c','#fbe983','#fad165',
  1379. '#92e1c0','#9fe1e7','#9fc6e7','#4986e7','#9a9cff','#b99aff',
  1380. '#c2c2c2','#cabdbf','#cca6ac','#f691b2','#cd74e6','#a47ae2',
  1381. '#444444'
  1382. ]
  1383. var button_defaults =
  1384. {
  1385. 'font' : {
  1386. values:['Arial', 'Courier', 'Comic Sans MS', 'Helvetica', 'Open Sans', 'Tahoma', 'Verdana'],
  1387. icon:'fa fa-font',
  1388. title:'Font'
  1389. },
  1390. 'fontSize' : {
  1391. values:{5:'Huge', 3:'Normal', 1:'Small'},
  1392. icon:'fa fa-text-height',
  1393. title:'Font Size'
  1394. },
  1395. 'bold' : {
  1396. icon : 'fa fa-bold',
  1397. title : 'Bold (Ctrl/Cmd+B)'
  1398. },
  1399. 'italic' : {
  1400. icon : 'fa fa-italic',
  1401. title : 'Italic (Ctrl/Cmd+I)'
  1402. },
  1403. 'strikethrough' : {
  1404. icon : 'fa fa-strikethrough',
  1405. title : 'Strikethrough'
  1406. },
  1407. 'underline' : {
  1408. icon : 'fa fa-underline',
  1409. title : 'Underline'
  1410. },
  1411. 'insertunorderedlist' : {
  1412. icon : 'fa fa-list-ul',
  1413. title : 'Bullet list'
  1414. },
  1415. 'insertorderedlist' : {
  1416. icon : 'fa fa-list-ol',
  1417. title : 'Number list'
  1418. },
  1419. 'outdent' : {
  1420. icon : 'fa fa-outdent',
  1421. title : 'Reduce indent (Shift+Tab)'
  1422. },
  1423. 'indent' : {
  1424. icon : 'fa fa-indent',
  1425. title : 'Indent (Tab)'
  1426. },
  1427. 'justifyleft' : {
  1428. icon : 'fa fa-align-left',
  1429. title : 'Align Left (Ctrl/Cmd+L)'
  1430. },
  1431. 'justifycenter' : {
  1432. icon : 'fa fa-align-center',
  1433. title : 'Center (Ctrl/Cmd+E)'
  1434. },
  1435. 'justifyright' : {
  1436. icon : 'fa fa-align-right',
  1437. title : 'Align Right (Ctrl/Cmd+R)'
  1438. },
  1439. 'justifyfull' : {
  1440. icon : 'fa fa-align-justify',
  1441. title : 'Justify (Ctrl/Cmd+J)'
  1442. },
  1443. 'createLink' : {
  1444. icon : 'fa fa-link',
  1445. title : 'Hyperlink',
  1446. button_text : 'Add',
  1447. placeholder : 'URL',
  1448. button_class : 'btn-primary'
  1449. },
  1450. 'unlink' : {
  1451. icon : 'fa fa-chain-broken',
  1452. title : 'Remove Hyperlink'
  1453. },
  1454. 'insertImage' : {
  1455. icon : 'fa fa-picture-o',
  1456. title : 'Insert picture',
  1457. button_text : '<i class="'+ ace.vars['icon'] + 'fa fa-file"></i> Choose Image &hellip;',
  1458. placeholder : 'Image URL',
  1459. button_insert : 'Insert',
  1460. button_class : 'btn-success',
  1461. button_insert_class : 'btn-primary',
  1462. choose_file: true //show the choose file button?
  1463. },
  1464. 'foreColor' : {
  1465. values : color_values,
  1466. title : 'Change Color'
  1467. },
  1468. 'backColor' : {
  1469. values : color_values,
  1470. title : 'Change Background Color'
  1471. },
  1472. 'undo' : {
  1473. icon : 'fa fa-undo',
  1474. title : 'Undo (Ctrl/Cmd+Z)'
  1475. },
  1476. 'redo' : {
  1477. icon : 'fa fa-repeat',
  1478. title : 'Redo (Ctrl/Cmd+Y)'
  1479. },
  1480. 'viewSource' : {
  1481. icon : 'fa fa-code',
  1482. title : 'View Source'
  1483. }
  1484. }
  1485. var toolbar_buttons =
  1486. options.toolbar ||
  1487. [
  1488. 'font',
  1489. null,
  1490. 'fontSize',
  1491. null,
  1492. 'bold',
  1493. 'italic',
  1494. 'strikethrough',
  1495. 'underline',
  1496. null,
  1497. 'insertunorderedlist',
  1498. 'insertorderedlist',
  1499. 'outdent',
  1500. 'indent',
  1501. null,
  1502. 'justifyleft',
  1503. 'justifycenter',
  1504. 'justifyright',
  1505. 'justifyfull',
  1506. null,
  1507. 'createLink',
  1508. 'unlink',
  1509. null,
  1510. 'insertImage',
  1511. null,
  1512. 'foreColor',
  1513. null,
  1514. 'undo',
  1515. 'redo',
  1516. null,
  1517. 'viewSource'
  1518. ]
  1519. this.each(function() {
  1520. var toolbar = ' <div class="wysiwyg-toolbar btn-toolbar center"> <div class="btn-group"> ';
  1521. for(var tb in toolbar_buttons) if(toolbar_buttons.hasOwnProperty(tb)) {
  1522. var button = toolbar_buttons[tb];
  1523. if(button === null){
  1524. toolbar += ' </div> <div class="btn-group"> ';
  1525. continue;
  1526. }
  1527. if(typeof button == "string" && button in button_defaults) {
  1528. button = button_defaults[button];
  1529. button.name = toolbar_buttons[tb];
  1530. } else if(typeof button == "object" && button.name in button_defaults) {
  1531. button = $.extend(button_defaults[button.name] , button);
  1532. }
  1533. else continue;
  1534. var className = "className" in button ? button.className : 'btn-default';
  1535. switch(button.name) {
  1536. case 'font':
  1537. toolbar += ' <a class="btn btn-sm '+className+' dropdown-toggle" data-toggle="dropdown" title="'+button.title+'"><i class="'+ ace.vars['icon'] + button.icon+'"></i><i class="' + ace.vars['icon'] + 'fa fa-angle-down icon-on-right"></i></a> ';
  1538. toolbar += ' <ul class="dropdown-menu dropdown-light dropdown-caret">';
  1539. for(var font in button.values)
  1540. if(button.values.hasOwnProperty(font))
  1541. toolbar += ' <li><a data-edit="fontName ' + button.values[font] +'" style="font-family:\''+ button.values[font] +'\'">'+button.values[font] + '</a></li> '
  1542. toolbar += ' </ul>';
  1543. break;
  1544. case 'fontSize':
  1545. toolbar += ' <a class="btn btn-sm '+className+' dropdown-toggle" data-toggle="dropdown" title="'+button.title+'"><i class="'+ ace.vars['icon'] + button.icon+'"></i>&nbsp;<i class="'+ ace.vars['icon'] + 'fa fa-angle-down icon-on-right"></i></a> ';
  1546. toolbar += ' <ul class="dropdown-menu dropdown-light dropdown-caret"> ';
  1547. for(var size in button.values)
  1548. if(button.values.hasOwnProperty(size))
  1549. toolbar += ' <li><a data-edit="fontSize '+size+'"><font size="'+size+'">'+ button.values[size] +'</font></a></li> '
  1550. toolbar += ' </ul> ';
  1551. break;
  1552. case 'createLink':
  1553. toolbar += ' <div class="btn-group"> <a class="btn btn-sm '+className+' dropdown-toggle" data-toggle="dropdown" title="'+button.title+'"><i class="'+ ace.vars['icon'] + button.icon+'"></i></a> ';
  1554. toolbar += ' <div class="dropdown-menu dropdown-caret dropdown-menu-right">\
  1555. <div class="input-group">\
  1556. <input class="form-control" placeholder="'+button.placeholder+'" type="text" data-edit="'+button.name+'" />\
  1557. <span class="input-group-btn">\
  1558. <button class="btn btn-sm '+button.button_class+'" type="button">'+button.button_text+'</button>\
  1559. </span>\
  1560. </div>\
  1561. </div> </div>';
  1562. break;
  1563. case 'insertImage':
  1564. toolbar += ' <div class="btn-group"> <a class="btn btn-sm '+className+' dropdown-toggle" data-toggle="dropdown" title="'+button.title+'"><i class="'+ ace.vars['icon'] + button.icon+'"></i></a> ';
  1565. toolbar += ' <div class="dropdown-menu dropdown-caret dropdown-menu-right">\
  1566. <div class="input-group">\
  1567. <input class="form-control" placeholder="'+button.placeholder+'" type="text" data-edit="'+button.name+'" />\
  1568. <span class="input-group-btn">\
  1569. <button class="btn btn-sm '+button.button_insert_class+'" type="button">'+button.button_insert+'</button>\
  1570. </span>\
  1571. </div>';
  1572. if( button.choose_file && 'FileReader' in window ) toolbar +=
  1573. '<div class="space-2"></div>\
  1574. <label class="center block no-margin-bottom">\
  1575. <button class="btn btn-sm '+button.button_class+' wysiwyg-choose-file" type="button">'+button.button_text+'</button>\
  1576. <input type="file" data-edit="'+button.name+'" />\
  1577. </label>'
  1578. toolbar += ' </div> </div>';
  1579. break;
  1580. case 'foreColor':
  1581. case 'backColor':
  1582. toolbar += ' <select class="hide wysiwyg_colorpicker" title="'+button.title+'"> ';
  1583. $.each(button.values, function (_, color) {
  1584. toolbar += ' <option value="' + color + '">' + color + '</option> ';
  1585. });
  1586. toolbar += ' </select> ';
  1587. toolbar += ' <input style="display:none;" disabled class="hide" type="text" data-edit="'+button.name+'" /> ';
  1588. break;
  1589. case 'viewSource':
  1590. toolbar += ' <a class="btn btn-sm '+className+'" data-view="source" title="'+button.title+'"><i class="'+ ace.vars['icon'] + button.icon+'"></i></a> ';
  1591. break;
  1592. default:
  1593. toolbar += ' <a class="btn btn-sm '+className+'" data-edit="'+button.name+'" title="'+button.title+'"><i class="'+ ace.vars['icon'] + button.icon+'"></i></a> ';
  1594. break;
  1595. }
  1596. }
  1597. toolbar += ' </div> ';
  1598. ////////////
  1599. var speech_input;
  1600. if (options.speech_button && 'onwebkitspeechchange' in (speech_input = document.createElement('input'))) {
  1601. toolbar += ' <input class="wysiwyg-speech-input" type="text" data-edit="inserttext" x-webkit-speech />';
  1602. }
  1603. speech_input = null;
  1604. ////////////
  1605. toolbar += ' </div> ';
  1606. //if we have a function to decide where to put the toolbar, then call that
  1607. if(options.toolbar_place) toolbar = options.toolbar_place.call(this, toolbar);
  1608. //otherwise put it just before our DIV
  1609. else toolbar = $(this).before(toolbar).prev();
  1610. toolbar.find('a[title]').tooltip({animation:false, container:'body'});
  1611. toolbar.find('.dropdown-menu input[type=text]').on('click', function() {return false})
  1612. .on('change', function() {$(this).closest('.dropdown-menu').siblings('.dropdown-toggle').dropdown('toggle')})
  1613. .on('keydown', function (e) {
  1614. if(e.which == 27) {
  1615. this.value = '';
  1616. $(this).change();
  1617. }
  1618. else if(e.which == 13) {
  1619. e.preventDefault();
  1620. e.stopPropagation();
  1621. $(this).change();
  1622. }
  1623. });
  1624. toolbar.find('input[type=file]').prev().on(ace.click_event, function (e) {
  1625. $(this).next().click();
  1626. });
  1627. toolbar.find('.wysiwyg_colorpicker').each(function() {
  1628. $(this).ace_colorpicker({pull_right:true}).change(function(){
  1629. $(this).nextAll('input').eq(0).val(this.value).change();
  1630. }).next().find('.btn-colorpicker').tooltip({title: this.title, animation:false, container:'body'})
  1631. });
  1632. var self = $(this);
  1633. //view source
  1634. var view_source = false;
  1635. toolbar.find('a[data-view=source]').on('click', function(e){
  1636. e.preventDefault();
  1637. if(!view_source) {
  1638. $('<textarea />')
  1639. .css({'width':self.outerWidth(), 'height':self.outerHeight()})
  1640. .val(self.html())
  1641. .insertAfter(self)
  1642. self.hide();
  1643. $(this).addClass('active');
  1644. }
  1645. else {
  1646. var textarea = self.next();
  1647. self.html(textarea.val()).show();
  1648. textarea.remove();
  1649. $(this).removeClass('active');
  1650. }
  1651. view_source = !view_source;
  1652. });
  1653. var $options = $.extend({}, { activeToolbarClass: 'active' , toolbarSelector : toolbar }, options.wysiwyg || {})
  1654. $(this).wysiwyg( $options );
  1655. });
  1656. return this;
  1657. }
  1658. })(window.jQuery);
  1659. ;/**
  1660. <b>Spinner</b>. A wrapper for FuelUX spinner element.
  1661. It's just a wrapper so you still need to include FuelUX spinner script first.
  1662. */
  1663. (function($ , undefined) {
  1664. //a wrapper for fuelux spinner
  1665. function Ace_Spinner(element , options) {
  1666. var max = options.max
  1667. max = (''+max).length
  1668. var width = parseInt(Math.max((max * 20 + 40) , 90))
  1669. var $element = $(element);
  1670. $element.addClass('spinbox-input form-control').wrap('<div class="ace-spinner middle">')
  1671. var $parent_div = $element.closest('.ace-spinner').spinbox(options).wrapInner("<div class='input-group'></div>")
  1672. var $spinner = $parent_div.data('fu.spinbox');
  1673. if(options.on_sides)
  1674. {
  1675. $element
  1676. .before('<div class="spinbox-buttons input-group-btn">\
  1677. <button type="button" class="btn spinbox-down btn-xs '+options.btn_down_class+'">\
  1678. <i class="'+ ace.vars['icon'] + options.icon_down+'"></i>\
  1679. </button>\
  1680. </div>')
  1681. .after('<div class="spinbox-buttons input-group-btn">\
  1682. <button type="button" class="btn spinbox-up btn-xs '+options.btn_up_class+'">\
  1683. <i class="'+ ace.vars['icon'] + options.icon_up+'"></i>\
  1684. </button>\
  1685. </div>');
  1686. $parent_div.addClass('touch-spinner')
  1687. $parent_div.css('width' , width+'px')
  1688. }
  1689. else {
  1690. $element
  1691. .after('<div class="spinbox-buttons input-group-btn">\
  1692. <button type="button" class="btn spinbox-up btn-xs '+options.btn_up_class+'">\
  1693. <i class="'+ ace.vars['icon'] + options.icon_up+'"></i>\
  1694. </button>\
  1695. <button type="button" class="btn spinbox-down btn-xs '+options.btn_down_class+'">\
  1696. <i class="'+ ace.vars['icon'] + options.icon_down+'"></i>\
  1697. </button>\
  1698. </div>')
  1699. if(ace.vars['touch'] || options.touch_spinner) {
  1700. $parent_div.addClass('touch-spinner')
  1701. $parent_div.css('width' , width+'px')
  1702. }
  1703. else {
  1704. $element.next().addClass('btn-group-vertical');
  1705. $parent_div.css('width' , width+'px')
  1706. }
  1707. }
  1708. $parent_div.on('changed', function(){
  1709. $element.trigger('change')//trigger the input's change event
  1710. });
  1711. this._call = function(name, arg) {
  1712. $spinner[name](arg);
  1713. }
  1714. }
  1715. $.fn.ace_spinner = function(option, value) {
  1716. var retval;
  1717. var $set = this.each(function() {
  1718. var $this = $(this);
  1719. var data = $this.data('ace_spinner');
  1720. var options = typeof option === 'object' && option;
  1721. if (!data) {
  1722. options = $.extend({}, $.fn.ace_spinner.defaults, option);
  1723. $this.data('ace_spinner', (data = new Ace_Spinner(this, options)));
  1724. }
  1725. if (typeof option === 'string') retval = data._call(option, value);
  1726. });
  1727. return (retval === undefined) ? $set : retval;
  1728. }
  1729. $.fn.ace_spinner.defaults = {
  1730. 'icon_up' : 'fa fa-chevron-up',
  1731. 'icon_down': 'fa fa-chevron-down',
  1732. 'on_sides': false,
  1733. 'btn_up_class': '',
  1734. 'btn_down_class' : '',
  1735. 'max' : 999,
  1736. 'touch_spinner': false
  1737. }
  1738. })(window.jQuery);
  1739. ;/**
  1740. <b>Treeview</b>. A wrapper for FuelUX treeview element.
  1741. It's just a wrapper so you still need to include FuelUX treeview script first.
  1742. */
  1743. (function($ , undefined) {
  1744. $.fn.aceTree = $.fn.ace_tree = function(options) {
  1745. var $options = {
  1746. 'open-icon' : ace.vars['icon'] + 'fa fa-folder-open',
  1747. 'close-icon' : ace.vars['icon'] + 'fa fa-folder',
  1748. 'selectable' : true,
  1749. 'selected-icon' : ace.vars['icon'] + 'fa fa-check',
  1750. 'unselected-icon' : ace.vars['icon'] + 'fa fa-times',
  1751. 'loadingHTML': 'Loading...'
  1752. }
  1753. $options = $.extend({}, $options, options)
  1754. this.each(function() {
  1755. var $this = $(this);
  1756. $this.addClass('tree').attr('role', 'tree');
  1757. $this.html(
  1758. '<li class="tree-branch hide" data-template="treebranch" role="treeitem" aria-expanded="false">\
  1759. <div class="tree-branch-header">\
  1760. <span class="tree-branch-name">\
  1761. <i class="icon-folder '+$options['close-icon']+'"></i>\
  1762. <span class="tree-label"></span>\
  1763. </span>\
  1764. </div>\
  1765. <ul class="tree-branch-children" role="group"></ul>\
  1766. <div class="tree-loader" role="alert">'+$options['loadingHTML']+'</div>\
  1767. </div>\
  1768. <li class="tree-item hide" data-template="treeitem" role="treeitem">\
  1769. <span class="tree-item-name">\
  1770. '+($options['unselected-icon'] == null ? '' : '<i class="icon-item '+$options['unselected-icon']+'"></i>')+'\
  1771. <span class="tree-label"></span>\
  1772. </span>\
  1773. </li>');
  1774. $this.addClass($options['selectable'] == true ? 'tree-selectable' : 'tree-unselectable');
  1775. $this.tree($options);
  1776. });
  1777. return this;
  1778. }
  1779. })(window.jQuery);
  1780. ;/**
  1781. <b>Wizard</b>. A wrapper for FuelUX wizard element.
  1782. It's just a wrapper so you still need to include FuelUX wizard script first.
  1783. */
  1784. (function($ , undefined) {
  1785. $.fn.aceWizard = $.fn.ace_wizard = function(options) {
  1786. this.each(function() {
  1787. var $this = $(this);
  1788. $this.wizard();
  1789. if(ace.vars['old_ie']) $this.find('ul.steps > li').last().addClass('last-child');
  1790. var buttons = (options && options['buttons']) ? $(options['buttons']) : $this.siblings('.wizard-actions').eq(0);
  1791. var $wizard = $this.data('fu.wizard');
  1792. $wizard.$prevBtn.remove();
  1793. $wizard.$nextBtn.remove();
  1794. $wizard.$prevBtn = buttons.find('.btn-prev').eq(0).on(ace.click_event, function(){
  1795. $wizard.previous();
  1796. }).attr('disabled', 'disabled');
  1797. $wizard.$nextBtn = buttons.find('.btn-next').eq(0).on(ace.click_event, function(){
  1798. $wizard.next();
  1799. }).removeAttr('disabled');
  1800. $wizard.nextText = $wizard.$nextBtn.text();
  1801. var step = options && ((options.selectedItem && options.selectedItem.step) || options.step);
  1802. if(step) {
  1803. $wizard.currentStep = step;
  1804. $wizard.setState();
  1805. }
  1806. });
  1807. return this;
  1808. }
  1809. })(window.jQuery);
  1810. ;/**
  1811. <b>Content Slider</b>. with custom content and elements based on Bootstrap modals.
  1812. */
  1813. (function($ , undefined) {
  1814. var $window = $(window);
  1815. function Aside(modal, settings) {
  1816. var $modal = $(modal);
  1817. var placement = 'right', vertical = false;
  1818. var backdrop = '', invisible_backdrop = false;
  1819. var options = {}
  1820. options.fixed = settings.fixed || $modal.attr('data-fixed') == 'true';
  1821. options.dark = settings.background || $modal.attr('data-background') == 'true';
  1822. options.offset = settings.offset || $modal.attr('data-offset') == 'true';
  1823. options.no_scroll = !settings.body_scroll || $modal.attr('data-body-scroll') == 'false';
  1824. options.transition = settings.transition !== false && $modal.attr('data-transition') !== 'false';
  1825. options.scroll_style = settings.scroll_style || ((options.dark ? 'scroll-white' : 'scroll-dark') + ' no-track');
  1826. var dialog = $modal.find('.modal-dialog');
  1827. var content = $modal.find('.modal-content');
  1828. this.initiate = function() {
  1829. modal.className = modal.className.replace(/(\s|^)aside\-(right|top|left|bottom)(\s|$)/ig , '$1$3');
  1830. placement = settings.placement || $modal.attr('data-placement');
  1831. if(placement) placement = $.trim(placement.toLowerCase());
  1832. else placement = 'right';
  1833. backdrop = settings.backdrop || $modal.attr('data-backdrop');
  1834. if( !(/right|top|left|bottom/.test(placement)) ) placement = 'right';
  1835. $modal.attr('data-placement', placement);
  1836. $modal.addClass('aside-' + placement);
  1837. if( /right|left/.test(placement) ) {
  1838. vertical = true;
  1839. $modal.addClass('aside-vc');//vertical
  1840. }
  1841. else $modal.addClass('aside-hz');//horizontal
  1842. if( options.fixed ) $modal.addClass('aside-fixed');
  1843. if( options.dark ) $modal.addClass('aside-dark');
  1844. if( options.offset ) $modal.addClass('navbar-offset');
  1845. if( !options.transition ) $modal.addClass('transition-off');
  1846. $modal.addClass('aside-hidden');
  1847. this.insideContainer();
  1848. /////////////////////////////
  1849. dialog = $modal.find('.modal-dialog');
  1850. content = $modal.find('.modal-content');
  1851. if(options.no_scroll) {
  1852. //don't allow body scroll when modal is open
  1853. $modal.on('mousewheel.aside DOMMouseScroll.aside touchmove.aside pointermove.aside', function(e) {
  1854. if( !$.contains(content[0], e.target) ) {
  1855. e.preventDefault();
  1856. return false;
  1857. }
  1858. })
  1859. }
  1860. if( backdrop === false || backdrop === 'false' ) {
  1861. $modal.addClass('no-backdrop');
  1862. }
  1863. else if(backdrop === 'invisible') invisible_backdrop = true;
  1864. }
  1865. this.show = function() {
  1866. $modal
  1867. .css('position', 'fixed')
  1868. .removeClass('aside-hidden');
  1869. }
  1870. this.hide = function() {
  1871. toggleButton();
  1872. if(ace.vars['transition'] && !$modal.hasClass('fade')) {
  1873. $modal.one('bsTransitionEnd', function() {
  1874. $modal.addClass('aside-hidden');
  1875. $modal.css('position', '');
  1876. }).emulateTransitionEnd(350);
  1877. }
  1878. }
  1879. this.shown = function() {
  1880. toggleButton();
  1881. $('body').removeClass('modal-open').css('padding-right', '');
  1882. if( invisible_backdrop ) {
  1883. try {
  1884. $modal.data('bs.modal').$backdrop.css('opacity', 0);
  1885. } catch(e){}
  1886. }
  1887. var size = !vertical ? dialog.height() : content.height();
  1888. if(!ace.vars['touch']) {
  1889. if(!content.hasClass('ace-scroll')) {
  1890. content.ace_scroll({
  1891. size: size,
  1892. reset: true,
  1893. mouseWheelLock: true,
  1894. lockAnyway: options.no_scroll,
  1895. styleClass: options.scroll_style,
  1896. 'observeContent': true,
  1897. 'hideOnIdle': !ace.vars['old_ie'],
  1898. 'hideDelay': 1500
  1899. })
  1900. }
  1901. }
  1902. else {
  1903. content.addClass('overflow-scroll').css('max-height', size+'px');
  1904. }
  1905. $window
  1906. .off('resize.modal.aside')
  1907. .on('resize.modal.aside', function() {
  1908. if(!ace.vars['touch']) {
  1909. content.ace_scroll('disable');//to get correct size when going from small window size to large size
  1910. var size = !vertical ? dialog.height() : content.height();
  1911. content
  1912. .ace_scroll('update', {'size': size})
  1913. .ace_scroll('enable')
  1914. .ace_scroll('reset');
  1915. }
  1916. else content.css('max-height', (!vertical ? dialog.height() : content.height())+'px');
  1917. }).triggerHandler('resize.modal.aside');
  1918. }
  1919. this.hidden = function() {
  1920. $window.off('.aside')
  1921. //$modal.off('.aside')
  1922. //
  1923. if( !ace.vars['transition'] || $modal.hasClass('fade') ) {
  1924. $modal.addClass('aside-hidden');
  1925. $modal.css('position', '');
  1926. }
  1927. }
  1928. this.insideContainer = function() {
  1929. var container = $('.main-container');
  1930. var dialog = $modal.find('.modal-dialog');
  1931. dialog.css({'right': '', 'left': ''});
  1932. if( container.hasClass('container') ) {
  1933. var flag = false;
  1934. if(vertical == true) {
  1935. dialog.css( placement, parseInt(($window.width() - container.width()) / 2) );
  1936. flag = true;
  1937. }
  1938. //strange firefox issue, not redrawing properly on window resize (zoom in/out)!!!!
  1939. //--- firefix is still having issue!
  1940. if(flag && ace.vars['firefox']) {
  1941. ace.helper.redraw(container[0]);
  1942. }
  1943. }
  1944. }
  1945. this.flip = function() {
  1946. var flipSides = {right : 'left', left : 'right', top: 'bottom', bottom: 'top'};
  1947. $modal.removeClass('aside-'+placement).addClass('aside-'+flipSides[placement]);
  1948. placement = flipSides[placement];
  1949. }
  1950. var toggleButton = function() {
  1951. var btn = $modal.find('.aside-trigger');
  1952. if(btn.length == 0) return;
  1953. btn.toggleClass('open');
  1954. var icon = btn.find(ace.vars['.icon']);
  1955. if(icon.length == 0) return;
  1956. icon.toggleClass(icon.attr('data-icon1') + " " + icon.attr('data-icon2'));
  1957. }
  1958. this.initiate();
  1959. $modal.appendTo('body');
  1960. }
  1961. $(document)
  1962. .on('show.bs.modal', '.modal.aside', function(e) {
  1963. $('.aside.in').modal('hide');//??? hide previous open ones?
  1964. $(this).ace_aside('show');
  1965. })
  1966. .on('hide.bs.modal', '.modal.aside', function(e) {
  1967. $(this).ace_aside('hide');
  1968. })
  1969. .on('shown.bs.modal', '.modal.aside', function(e) {
  1970. $(this).ace_aside('shown');
  1971. })
  1972. .on('hidden.bs.modal', '.modal.aside', function(e) {
  1973. $(this).ace_aside('hidden');
  1974. })
  1975. $(window).on('resize.aside_container', function() {
  1976. $('.modal.aside').ace_aside('insideContainer');
  1977. });
  1978. $(document).on('settings.ace.aside', function(e, event_name) {
  1979. if(event_name == 'main_container_fixed') $('.modal.aside').ace_aside('insideContainer');
  1980. });
  1981. $.fn.aceAside = $.fn.ace_aside = function (option, value) {
  1982. var method_call;
  1983. var $set = this.each(function () {
  1984. var $this = $(this);
  1985. var data = $this.data('ace_aside');
  1986. var options = typeof option === 'object' && option;
  1987. if (!data) $this.data('ace_aside', (data = new Aside(this, options)));
  1988. if (typeof option === 'string' && typeof data[option] === 'function') {
  1989. if(value instanceof Array) method_call = data[option].apply(data, value);
  1990. else method_call = data[option](value);
  1991. }
  1992. });
  1993. return (method_call === undefined) ? $set : method_call;
  1994. };
  1995. $('.modal.aside').ace_aside();
  1996. })(window.jQuery);