fa-multi-button.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636
  1. /*!jQuery FA multi button*/
  2. /**
  3. * Modern toggle, push button, dimmer or just a signal indicator
  4. *
  5. * Version: 1.3.0
  6. * Requires: jQuery v1.7+
  7. *
  8. * Copyright (c) 2015-2017 Mario Stephan
  9. * Under MIT License (http://www.opensource.org/licenses/mit-license.php)
  10. *
  11. * Thanks to phoxoey
  12. */
  13. "use strict";
  14. /* global jQuery:true */
  15. (function ($) {
  16. $.fn.famultibutton = function (pOptions) {
  17. if (this.length > 1) {
  18. this.each(function () {
  19. $(this).famultibutton(pOptions);
  20. });
  21. return this;
  22. }
  23. // private variables;
  24. var elem = this;
  25. var faElem;
  26. var state = false;
  27. // private for dimmer
  28. var canvasScale;
  29. var objTimer;
  30. var isRunning = false;
  31. var resStepValues = [0, 10, 40, 80, 120, 140, 150, 160, 180, 200, 240, 260, 280, 300, 320, 420, 430, 440, 450, 460, 470];
  32. var dragy = 0;
  33. var currVal = 0;
  34. var diff = 0;
  35. var baseTop = 0;
  36. var posy = 0;
  37. var isDrag = false;
  38. var isDown = false;
  39. var timer;
  40. // setup options
  41. var defaultOptions = {
  42. backgroundIcon: 'fa-circle',
  43. classes: ['fa-2x'],
  44. icon: 'fa-power-off',
  45. offColor: '#2A2A2A',
  46. offBackgroundColor: '#505050',
  47. onColor: '#2A2A2A',
  48. onBackgroundColor: '#aa6900',
  49. mode: 'toggle', //toggle, push, signal, dimmer
  50. toggleOn: null,
  51. toggleOff: null,
  52. valueChanged: null,
  53. progressWidth: 15,
  54. timeout: 0,
  55. max: 100,
  56. min: 0,
  57. step: 1,
  58. };
  59. var options = $.extend({}, defaultOptions, pOptions);
  60. // private functions;
  61. var intialize = function () {
  62. options = $.extend({}, options, elem.data());
  63. var content = (elem.html() !== elem.text()) ?
  64. jQuery('<div>', {}).html(elem.html()).children().detach() :
  65. jQuery('<div>', {}).text(elem.text());
  66. if (options['onColor'] !== 'none' && options['offColor'] !== 'none') {
  67. content.attr('id', 'fg');
  68. }
  69. content.addClass('fa-stack-1x');
  70. elem.html('');
  71. elem.bi = options['backgroundIcon'];
  72. elem.fi = options['icon'];
  73. faElem = $('<div/>', {
  74. class: 'famultibutton'
  75. });
  76. faElem.addClass('fa-stack');
  77. elem.bg = jQuery('<i/>', {
  78. 'id': 'bg',
  79. 'class': 'fa fa-stack-2x'
  80. }).addClass(elem.bi);
  81. elem.fg = jQuery('<i/>', {
  82. 'id': 'fg',
  83. 'class': 'fa fa-stack-1x'
  84. }).addClass(elem.fi);
  85. if (options['classes'] && options['classes'].length > 0) {
  86. for (var i = 0, len = options['classes'].length; i < len; i++) {
  87. faElem.addClass(options['classes'][i]);
  88. }
  89. }
  90. elem.bg.appendTo(faElem);
  91. elem.fg.appendTo(faElem);
  92. content.appendTo(faElem);
  93. faElem.appendTo(elem);
  94. elem.o = options;
  95. elem.w = faElem.width();
  96. elem.h = faElem.height();
  97. setOff();
  98. if (options['mode'] == 'dimmer') {
  99. canvasScale = $('<canvas>').attr({
  100. id: 'scale',
  101. height: elem.h + 'px',
  102. width: elem.w + 'px'
  103. }).appendTo(faElem);
  104. baseTop = parseInt(canvasScale.offset().top) - parseInt(faElem.offset().top);
  105. drawScale();
  106. moveScale();
  107. }
  108. elem.data("famultibutton", elem);
  109. initEvents();
  110. return elem;
  111. };
  112. function setOn() {
  113. state = true;
  114. elem.bg.css("color", options['onBackgroundColor']);
  115. elem.fg.css("color", options['onColor']);
  116. faElem.addClass('active');
  117. elem.trigger('setOn');
  118. }
  119. function setOff() {
  120. state = false;
  121. elem.bg.css("color", options['offBackgroundColor']);
  122. elem.fg.css("color", options['offColor']);
  123. faElem.removeClass('active');
  124. elem.trigger('setOff');
  125. }
  126. function setForegroundColor(color) {
  127. elem.fg.css("color", color);
  128. }
  129. function setBackgroundColor(color) {
  130. elem.bg.css("color", color);
  131. }
  132. function setForegroundIcon(icon) {
  133. elem.fg.removeClass(elem.fi);
  134. elem.fi = icon;
  135. elem.fg.addClass(elem.fi);
  136. }
  137. function setBackgroundIcon(icon) {
  138. elem.bg.removeClass(elem.bi);
  139. elem.bi = icon;
  140. elem.bg.addClass(elem.bi);
  141. }
  142. function fadeOff() {
  143. $('<div />').animate({
  144. 'width': 100
  145. }, {
  146. duration: 700,
  147. easing: 'swing',
  148. // Fade the colors in the step function
  149. step: function (now, fx) {
  150. var completion = (now - fx.start) / (fx.end - fx.start);
  151. elem.bg.css('color', getGradientColor(
  152. options['onBackgroundColor'],
  153. options['offBackgroundColor'],
  154. completion));
  155. elem.fg.css('color', getGradientColor(
  156. options['onColor'],
  157. options['offColor'],
  158. completion));
  159. },
  160. complete: function () {
  161. if (state === true) {
  162. setOn();
  163. }
  164. }
  165. });
  166. }
  167. // helper functions for color fade out
  168. function rgbToHex(rgb) {
  169. var tokens = rgb.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i);
  170. return (tokens && tokens.length === 4) ? "#" +
  171. ("0" + parseInt(tokens[1], 10).toString(16)).slice(-2) +
  172. ("0" + parseInt(tokens[2], 10).toString(16)).slice(-2) +
  173. ("0" + parseInt(tokens[3], 10).toString(16)).slice(-2) : rgb;
  174. }
  175. function getGradientColor(start_color, end_color, percent) {
  176. // strip the leading # if it's there
  177. start_color = rgbToHex(start_color).replace(/^\s*#|\s*$/g, '');
  178. end_color = rgbToHex(end_color).replace(/^\s*#|\s*$/g, '');
  179. // convert 3 char codes --> 6, e.g. `E0F` --> `EE00FF`
  180. if (start_color.length == 3) {
  181. start_color = start_color.replace(/(.)/g, '$1$1');
  182. }
  183. if (end_color.length == 3) {
  184. end_color = end_color.replace(/(.)/g, '$1$1');
  185. }
  186. // get colors
  187. var start_red = parseInt(start_color.substr(0, 2), 16),
  188. start_green = parseInt(start_color.substr(2, 2), 16),
  189. start_blue = parseInt(start_color.substr(4, 2), 16);
  190. var end_red = parseInt(end_color.substr(0, 2), 16),
  191. end_green = parseInt(end_color.substr(2, 2), 16),
  192. end_blue = parseInt(end_color.substr(4, 2), 16);
  193. // calculate new color
  194. var diff_red = end_red - start_red;
  195. var diff_green = end_green - start_green;
  196. var diff_blue = end_blue - start_blue;
  197. diff_red = ((diff_red * percent) + start_red).toString(16).split('.')[0];
  198. diff_green = ((diff_green * percent) + start_green).toString(16).split('.')[0];
  199. diff_blue = ((diff_blue * percent) + start_blue).toString(16).split('.')[0];
  200. // ensure 2 digits by color
  201. if (diff_red.length == 1)
  202. diff_red = '0' + diff_red;
  203. if (diff_green.length == 1)
  204. diff_green = '0' + diff_green;
  205. if (diff_blue.length == 1)
  206. diff_blue = '0' + diff_blue;
  207. return '#' + diff_red + diff_green + diff_blue;
  208. }
  209. function setProgressValue(value) {
  210. var $canvasProgress = elem.find('canvas#progress');
  211. if (value > 0) {
  212. if ($canvasProgress.length === 0) {
  213. $canvasProgress = $('<canvas>').attr({
  214. id: 'progress'
  215. }).appendTo(faElem);
  216. }
  217. var canvas = $canvasProgress[0];
  218. if (canvas) {
  219. canvas.height = elem.h;
  220. canvas.width = elem.w;
  221. var x = canvas.width / 2;
  222. var y = canvas.height / 2;
  223. if (canvas.getContext) {
  224. var c = canvas.getContext('2d');
  225. c.beginPath();
  226. c.strokeStyle = options.onColor;
  227. c.arc(x, y, x * ((-0.4 / 90) * Number(options.progressWidth) + 0.8), -0.5 * Math.PI, (-0.5 + value * 2) *
  228. Math.PI, false);
  229. c.lineWidth = x * 0.80 * options.progressWidth / 100;
  230. c.stroke();
  231. }
  232. }
  233. } else {
  234. elem.find('canvas#progress').remove();
  235. }
  236. }
  237. function tickTimer() {
  238. clearTimeout(objTimer);
  239. currVal = (diff > 0) ? currVal -= options['step'] : currVal += options['step'];
  240. if (currVal > options['max']) currVal = options['max'];
  241. if (currVal < options['min']) currVal = options['min'];
  242. drawScale();
  243. var d = (resStepValues[Math.abs(diff)]);
  244. objTimer = setTimeout(function () {
  245. tickTimer();
  246. }, 500 - d);
  247. }
  248. function drawScale() {
  249. var canvas = canvasScale[0];
  250. canvas.height = elem.h;
  251. canvas.width = elem.w;
  252. if (canvas.getContext) {
  253. var context = canvas.getContext('2d');
  254. context.strokeStyle = options['offBackgroundColor'];
  255. var max = options['max'];
  256. var min = options['min'];
  257. var valPosition = canvas.height - Math.round(canvas.height * (currVal - min) / (max - min));
  258. for (var i = 0; i < canvas.height; i += 4) {
  259. context.lineWidth = 1;
  260. context.beginPath();
  261. context.moveTo(5, i);
  262. context.lineTo(25, i);
  263. context.stroke();
  264. }
  265. context.strokeStyle = (state) ? options['onBackgroundColor'] :
  266. getGradientColor(options['offBackgroundColor'], '#ffffff', 0.4);
  267. context.lineWidth = 3;
  268. context.beginPath();
  269. context.moveTo(5, valPosition);
  270. context.lineTo(25, valPosition);
  271. context.stroke();
  272. context.fillStyle = getGradientColor(options['offBackgroundColor'], '#ffffff', 0.4);
  273. context.font = "10px sans-serif";
  274. context.fillText(currVal, 30, canvas.height);
  275. }
  276. }
  277. function moveScale() {
  278. canvasScale.css({
  279. position: 'absolute',
  280. 'z-index': -1,
  281. });
  282. if (isDrag) {
  283. canvasScale.animate({
  284. left: -elem.w * 0.6 + 'px'
  285. });
  286. } else {
  287. canvasScale.animate({
  288. left: elem.w / 5 + 'px',
  289. top: '0px'
  290. });
  291. }
  292. }
  293. function getAndroidVersion(ua) {
  294. ua = (ua || navigator.userAgent).toLowerCase();
  295. var match = ua.match(/android\s([0-9\.]*)/);
  296. return match ? match[1] : false;
  297. }
  298. function initEvents() {
  299. var touch_pos_x, touch_pos_y;
  300. var android = getAndroidVersion();
  301. var iOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
  302. var onlyTouch = ((android && parseFloat(android) < 5) || iOS);
  303. var clickEventType = (onlyTouch) ? 'touchstart' : 'touchstart mousedown';
  304. var moveEventType = ((onlyTouch) ? 'touchmove' : 'touchmove mousemove');
  305. var releaseEventType = ((onlyTouch) ? 'touchend' : 'touchend mouseup');
  306. var leaveEventType = ((onlyTouch) ? 'touchleave' : 'touchleave mouseout');
  307. var lastState;
  308. if (options['mode'] === 'push') {
  309. faElem.on(clickEventType, function (e) {
  310. //e.preventDefault();
  311. e.stopImmediatePropagation();
  312. touch_pos_y = $(window).scrollTop();
  313. touch_pos_x = $(window).scrollLeft();
  314. });
  315. faElem.on(releaseEventType, function (e) {
  316. e.preventDefault();
  317. e.stopImmediatePropagation();
  318. if (Math.abs(touch_pos_y - $(window).scrollTop()) > 3 ||
  319. (Math.abs(touch_pos_x - $(window).scrollLeft()) > 3)) return;
  320. lastState = state;
  321. setOn();
  322. if (typeof options['toggleOn'] === 'function') {
  323. options['toggleOn'].call(this);
  324. }
  325. state = false;
  326. setTimeout(function () {
  327. fadeOff();
  328. }, 200);
  329. if (lastState === true) {
  330. setTimeout(function () {
  331. setOn();
  332. }, 1000);
  333. }
  334. elem.trigger('clicked');
  335. return false;
  336. });
  337. } else if (options['mode'] == 'updown') {
  338. faElem.on(clickEventType, function (e) {
  339. e.preventDefault();
  340. e.stopImmediatePropagation();
  341. lastState = state;
  342. setOn();
  343. if (typeof options['toggleOn'] === 'function') {
  344. options['toggleOn'].call(this);
  345. }
  346. });
  347. faElem.on(releaseEventType, function (e) {
  348. e.preventDefault();
  349. e.stopImmediatePropagation();
  350. if (typeof options['toggleOff'] === 'function') {
  351. options['toggleOff'].call(this);
  352. }
  353. state = false;
  354. setTimeout(function () {
  355. fadeOff();
  356. }, 200);
  357. if (lastState === true) {
  358. setTimeout(function () {
  359. setOn();
  360. }, 1000);
  361. }
  362. elem.trigger('clicked');
  363. return false;
  364. });
  365. } else if (options['mode'] == 'toggle') {
  366. faElem.on(clickEventType, function (e) {
  367. e.stopImmediatePropagation();
  368. touch_pos_y = $(window).scrollTop();
  369. touch_pos_x = $(window).scrollLeft();
  370. });
  371. faElem.on(releaseEventType, function (e) {
  372. e.preventDefault();
  373. e.stopImmediatePropagation();
  374. if (Math.abs(touch_pos_y - $(window).scrollTop()) > 3 ||
  375. (Math.abs(touch_pos_x - $(window).scrollLeft()) > 3)) return;
  376. if (state) {
  377. setOff();
  378. if (typeof options['toggleOff'] === 'function') {
  379. options['toggleOff'].call(this);
  380. }
  381. if (options['timeout'] > 0) {
  382. timer = setTimeout(function () {
  383. setOn();
  384. }, options['timeout']);
  385. }
  386. } else {
  387. setOn();
  388. if (typeof options['toggleOn'] === 'function') {
  389. options['toggleOn'].call(this);
  390. }
  391. if (options['timeout'] > 0) {
  392. timer = setTimeout(function () {
  393. setOff();
  394. }, options['timeout']);
  395. }
  396. }
  397. elem.trigger('clicked');
  398. return false;
  399. });
  400. } else if (options['mode'] == 'dimmer') {
  401. faElem.on(clickEventType, function (e) {
  402. e.preventDefault();
  403. e.stopImmediatePropagation();
  404. var event = e.originalEvent;
  405. dragy = event.touches ? event.touches[0].clientY : e.pageY;
  406. diff = 0;
  407. isDown = true;
  408. });
  409. faElem.on(leaveEventType, function (e) {
  410. e.preventDefault();
  411. e.stopImmediatePropagation();
  412. if (isDrag) {
  413. isDrag = false;
  414. faElem.animate({
  415. top: 0
  416. });
  417. clearInterval(objTimer);
  418. isRunning = false;
  419. moveScale();
  420. }
  421. isDown = false;
  422. });
  423. faElem.on(releaseEventType, function (e) {
  424. e.preventDefault();
  425. e.stopImmediatePropagation();
  426. if (isDrag) {
  427. isDrag = false;
  428. faElem.animate({
  429. top: 0
  430. });
  431. clearTimeout(objTimer);
  432. isRunning = false;
  433. if (typeof options['valueChanged'] === 'function') {
  434. options['valueChanged'].call(this, currVal);
  435. }
  436. } else {
  437. if (state) {
  438. setOff();
  439. if (typeof options['toggleOff'] === 'function') {
  440. options['toggleOff'].call(this);
  441. }
  442. if (options['timeout'] > 0) {
  443. timer = setTimeout(function () {
  444. setOn();
  445. }, options['timeout']);
  446. }
  447. } else {
  448. setOn();
  449. if (typeof options['toggleOn'] === 'function') {
  450. options['toggleOn'].call(this);
  451. }
  452. if (options['timeout'] > 0) {
  453. timer = setTimeout(function () {
  454. setOff();
  455. }, options['timeout']);
  456. }
  457. }
  458. }
  459. isDrag = false;
  460. isDown = false;
  461. moveScale();
  462. drawScale();
  463. elem.trigger('clicked');
  464. return false;
  465. });
  466. faElem.on(moveEventType, function (e) {
  467. e.preventDefault();
  468. e.stopImmediatePropagation();
  469. if (isDown)
  470. isDrag = true;
  471. var event = e.originalEvent;
  472. posy = event.touches ? event.touches[0].clientY : e.pageY;
  473. diff = posy - dragy;
  474. if (diff > 20) diff = 20;
  475. if (diff < -20) diff = -20;
  476. if (isDrag) {
  477. this.style.top = diff + "px";
  478. var canvas = canvasScale[0];
  479. canvas.style.top = -diff + 'px';
  480. if (!isRunning) {
  481. moveScale();
  482. tickTimer();
  483. isRunning = true;
  484. }
  485. }
  486. });
  487. }
  488. }
  489. // public functions;
  490. this.setOn = function () {
  491. clearTimeout(timer);
  492. setOn();
  493. };
  494. this.setOff = function () {
  495. clearTimeout(timer);
  496. setOff();
  497. };
  498. this.getState = function () {
  499. return state;
  500. };
  501. this.getValue = function () {
  502. return currVal;
  503. };
  504. this.setDimValue = function (val) {
  505. currVal = val;
  506. drawScale();
  507. };
  508. this.setProgressValue = function (val) {
  509. setProgressValue(val);
  510. };
  511. this.setForegroundColor = function (color) {
  512. setForegroundColor(color);
  513. };
  514. this.setBackgroundColor = function (color) {
  515. setBackgroundColor(color);
  516. };
  517. this.setForegroundIcon = function (icon) {
  518. setForegroundIcon(icon);
  519. };
  520. this.setBackgroundIcon = function (icon) {
  521. setBackgroundIcon(icon);
  522. };
  523. return intialize();
  524. };
  525. })(jQuery);