fa-multi-button.js 21 KB

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