ace-elements.js 73 KB

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