bobjoll 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/README.md +35 -0
  2. package/package.json +25 -0
  3. package/scss/layout/_footer.scss +10 -0
  4. package/scss/layout/_header.scss +10 -0
  5. package/scss/modules/_fonts.scss +26 -0
  6. package/scss/modules/_reset.scss +219 -0
  7. package/scss/modules/bourbon/addons/_clearfix.scss +25 -0
  8. package/scss/modules/bourbon/addons/_ellipsis.scss +30 -0
  9. package/scss/modules/bourbon/addons/_position.scss +48 -0
  10. package/scss/modules/bourbon/addons/_prefixer.scss +66 -0
  11. package/scss/modules/bourbon/addons/_size.scss +51 -0
  12. package/scss/modules/bourbon/addons/_timing-functions.scss +34 -0
  13. package/scss/modules/bourbon/addons/_triangle.scss +63 -0
  14. package/scss/modules/bourbon/css3/_calc.scss +4 -0
  15. package/scss/modules/bourbon/css3/_flex-box.scss +287 -0
  16. package/scss/modules/bourbon/css3/_keyframes.scss +36 -0
  17. package/scss/modules/bourbon/css3/_linear-gradient.scss +38 -0
  18. package/scss/modules/bourbon/css3/_placeholder.scss +8 -0
  19. package/scss/modules/bourbon/css3/_selection.scss +42 -0
  20. package/scss/modules/bourbon/css3/_transition.scss +71 -0
  21. package/scss/modules/mixins/_component.scss +9 -0
  22. package/scss/modules/mixins/_grid.scss +75 -0
  23. package/scss/modules/mixins/_helpers.scss +224 -0
  24. package/scss/modules/variables/_colors.scss +447 -0
  25. package/scss/modules/variables/_general.scss +235 -0
  26. package/scss/partials/_accordion-v1-0.scss +165 -0
  27. package/scss/partials/_autocomplete-v1-0.scss +55 -0
  28. package/scss/partials/_general-v1-0.scss +51 -0
  29. package/scss/partials/_grid-v1-0.scss +109 -0
  30. package/scss/partials/_helper-v1-0.scss +299 -0
  31. package/scss/partials/_icon-v2-0.scss +323 -0
  32. package/scss/partials/_list-v1-0.scss +100 -0
  33. package/scss/partials/_modal-v1-0.scss +159 -0
  34. package/scss/partials/_notification-v1-1.scss +297 -0
  35. package/scss/partials/_progress-bar-v1.0.scss +25 -0
  36. package/scss/partials/_range-v1.0.scss +75 -0
  37. package/scss/partials/_tooltipFixed-v1.0.scss +128 -0
  38. package/scss/partials/_typography-v1-0.scss +201 -0
  39. package/scss/partials/animations/_fade.scss +23 -0
  40. package/scss/partials/animations/_rotate.scss +11 -0
  41. package/scss/partials/animations/_scale.scss +23 -0
  42. package/scss/partials/animations/_slide.scss +31 -0
  43. package/scss/partials/button-v4-0/_component.scss +304 -0
  44. package/scss/partials/form/_checkbox-and-radio-v1-0.scss +187 -0
  45. package/scss/partials/form/_dropdowns-v1-0.scss +323 -0
  46. package/scss/partials/form/_general-v1-0.scss +166 -0
  47. package/scss/partials/form/_group-v1-0.scss +157 -0
  48. package/scss/partials/form/_password-v1-0.scss +28 -0
  49. package/scss/partials/form/_switch-v1-0.scss +128 -0
  50. package/scss/partials/form/_upload-v1-0.scss +91 -0
  51. package/ts/library/common.ts +30 -0
  52. package/ts/library/cookie.ts +47 -0
  53. package/ts/library/delegate.ts +122 -0
  54. package/ts/library/dom.ts +124 -0
  55. package/ts/library/event.ts +138 -0
  56. package/ts/library/extend.js +32 -0
  57. package/ts/library/gr/dom.q.ts +12 -0
  58. package/ts/library/gr/social/dependency/twitter_pu.js +66 -0
  59. package/ts/library/gr/social/facebook.ts +154 -0
  60. package/ts/library/gr/social/google.ts +127 -0
  61. package/ts/library/gr/social/index.ts +35 -0
  62. package/ts/library/gr/social/twitter.ts +65 -0
  63. package/ts/library/helpers.ts +9 -0
  64. package/ts/library/number-abbreviate.js +57 -0
  65. package/ts/library/settings.ts +7 -0
  66. package/ts/library/storage.ts +131 -0
  67. package/ts/library/svg4everybody.legacy.js +122 -0
  68. package/ts/partials/accordion-v1.0.ts +104 -0
  69. package/ts/partials/accordionTabs-v1.0.ts +27 -0
  70. package/ts/partials/alert-v1.0.ts +51 -0
  71. package/ts/partials/copy-v1.0.ts +17 -0
  72. package/ts/partials/countdown-v1.0.ts +119 -0
  73. package/ts/partials/dropdown-v1.0.ts +247 -0
  74. package/ts/partials/hbs-v1.0.ts +9 -0
  75. package/ts/partials/modal-v1.0.ts +213 -0
  76. package/ts/partials/notifications-v1.1.ts +376 -0
  77. package/ts/partials/notify-v1.0.ts +746 -0
  78. package/ts/partials/password-v1.0.ts +19 -0
  79. package/ts/partials/popover-v1.0.ts +125 -0
  80. package/ts/partials/progress-bar-v1.0.ts +29 -0
  81. package/ts/partials/scroll-v1.0.ts +169 -0
  82. package/ts/partials/scrollable-v1.0.ts +90 -0
  83. package/ts/partials/tabs-v1.0.ts +79 -0
  84. package/ts/partials/tags-v1.0.ts +21 -0
  85. package/ts/partials/trigger-v2.0.ts +155 -0
  86. package/ts/partials/upload-v1.0.ts +17 -0
  87. package/ts/views/hbs/alert-v1.0/element.html.hbs +35 -0
  88. package/ts/views/hbs/countdown-v1.0/countdown-inner.hbs +39 -0
  89. package/ts/views/hbs/countdown-v1.0/countdown.hbs +4 -0
  90. package/ts/views/hbs/dropdown-v1.0/element.html.hbs +70 -0
  91. package/ts/views/hbs/helpers.js +58 -0
  92. package/ts/views/hbs/modal-v1.0/element.html.hbs +17 -0
  93. package/ts/views/hbs/notification-v1.1/element-disable.html.hbs +26 -0
  94. package/ts/views/hbs/notification-v1.1/element.html.hbs +43 -0
  95. package/ts/views/hbs/notification-v1.1/wrapper.html.hbs +4 -0
@@ -0,0 +1,131 @@
1
+ function getStorage(storageType: string): Storage | null {
2
+ try {
3
+ var storage = (window as any)[storageType + 'Storage'],
4
+ x = '__storage_test__';
5
+ storage.setItem(x, x);
6
+ storage.removeItem(x);
7
+ return storage;
8
+ }
9
+ catch(e) {
10
+ return null;
11
+ }
12
+ }
13
+
14
+ export class ClientStorage
15
+ {
16
+ private backend: Storage | null;
17
+ private dummy: { [key: string]: string } = {};
18
+
19
+ constructor(storageType: 'local' | 'session') {
20
+ this.backend = getStorage(storageType);
21
+ if (!this.backend) {
22
+ this.dummy = {};
23
+ }
24
+ }
25
+
26
+ supported(): boolean {
27
+ return !!this.backend;
28
+ }
29
+
30
+ removeItem(namespace: string, key: string) {
31
+ const k = namespace !== '' ? `${namespace}/${key}` : key;
32
+ if (this.backend) {
33
+ this.backend.removeItem(k);
34
+ }
35
+ else {
36
+ this.dummy[k];
37
+ }
38
+ }
39
+
40
+ getAll(namespace: string): {key: string; value: any}[] {
41
+ let namespaceStored: {key: string; value: any}[] = [];
42
+
43
+ if (this.backend) {
44
+ for (let i = 0; i < this.backend.length; i++) {
45
+ let key = this.backend.key(i);
46
+
47
+ if (key && key.match(/namespace/i)) {
48
+ let value = this.get(namespace, key);
49
+
50
+ if (value) {
51
+ namespaceStored.push({key: key, value: value});
52
+ }
53
+ }
54
+ }
55
+ }
56
+
57
+ return namespaceStored;
58
+ }
59
+
60
+ getAllKeys(namespace: string): string[] {
61
+ let namespaceKeys: string[] = [];
62
+
63
+ if (this.backend) {
64
+ for (let i = 0; i < this.backend.length; i++) {
65
+ let key = this.backend.key(i);
66
+
67
+ if (key && key.match(new RegExp(namespace, 'i'))) {
68
+ namespaceKeys.push(key);
69
+ }
70
+ }
71
+ }
72
+
73
+ return namespaceKeys;
74
+ }
75
+
76
+ getItem(namespace: string, key: string): string | null {
77
+ const k = namespace !== '' ? `${namespace}/${key}` : key;
78
+ return this.backend ? this.backend.getItem(k) : this.dummy[k];
79
+ }
80
+
81
+ setItem(namespace: string, key: string, value: string) {
82
+ const k = namespace !== '' ? `${namespace}/${key}` : key;
83
+ if (this.backend) {
84
+ this.backend.setItem(k, value);
85
+ }
86
+ else {
87
+ this.dummy[k] = value;
88
+ }
89
+ }
90
+
91
+ remove(namespace: string, key: string): any | null {
92
+ this.removeItem(namespace, key);
93
+ }
94
+
95
+ get(namespace: string, key: string): any | null {
96
+ return JSON.parse(this.getItem(namespace, key) || 'null');
97
+ }
98
+
99
+ set(namespace: string, key: string, value: any) {
100
+ this.setItem(namespace, key, JSON.stringify(value));
101
+ }
102
+
103
+ all(namespace: string) {
104
+ return this.getAll(namespace);
105
+ }
106
+
107
+ keys(namespace: string) {
108
+ return this.getAllKeys(namespace);
109
+ }
110
+
111
+ expired(namespace: string, key: string, expire?: Date) {
112
+ const item = this.getItem(namespace, key);
113
+ const now = new Date();
114
+
115
+ if (!item || now.getTime() > parseInt(item)) {
116
+ if (!expire) {
117
+ expire = new Date();
118
+ expire.setDate(expire.getDate() + 1);
119
+ expire.setHours(0,0,0,0);
120
+ }
121
+
122
+ this.setItem(namespace, key, JSON.stringify(expire.getTime()));
123
+ return true;
124
+ } else {
125
+ return false;
126
+ }
127
+ }
128
+ }
129
+
130
+ export let localStorage = new ClientStorage('local');
131
+ export let sessionStorage = new ClientStorage('session');
@@ -0,0 +1,122 @@
1
+ !function(root, factory) {
2
+ "function" == typeof define && define.amd ? // AMD. Register as an anonymous module unless amdModuleId is set
3
+ define([], function() {
4
+ return root.svg4everybody = factory();
5
+ }) : "object" == typeof module && module.exports ? // Node. Does not work with strict CommonJS, but
6
+ // only CommonJS-like environments that support module.exports,
7
+ // like Node.
8
+ module.exports = factory() : root.svg4everybody = factory();
9
+ }(this, function() {
10
+ /*! svg4everybody v2.1.4 | github.com/jonathantneal/svg4everybody */
11
+ function embed(parent, svg, target) {
12
+ // if the target exists
13
+ if (target) {
14
+ // create a document fragment to hold the contents of the target
15
+ var fragment = document.createDocumentFragment(), viewBox = !svg.hasAttribute("viewBox") && target.getAttribute("viewBox");
16
+ // conditionally set the viewBox on the svg
17
+ viewBox && svg.setAttribute("viewBox", viewBox);
18
+ // copy the contents of the clone into the fragment
19
+ for (// clone the target
20
+ var clone = target.cloneNode(!0); clone.childNodes.length; ) {
21
+ fragment.appendChild(clone.firstChild);
22
+ }
23
+ // append the fragment into the svg
24
+ parent.appendChild(fragment);
25
+ }
26
+ }
27
+ function loadreadystatechange(xhr) {
28
+ // listen to changes in the request
29
+ xhr.onreadystatechange = function() {
30
+ // if the request is ready
31
+ if (4 === xhr.readyState) {
32
+ // get the cached html document
33
+ var cachedDocument = xhr._cachedDocument;
34
+ // ensure the cached html document based on the xhr response
35
+ cachedDocument || (cachedDocument = xhr._cachedDocument = document.implementation.createHTMLDocument(""),
36
+ cachedDocument.body.innerHTML = xhr.responseText, xhr._cachedTarget = {}), // clear the xhr embeds list and embed each item
37
+ xhr._embeds.splice(0).map(function(item) {
38
+ // get the cached target
39
+ var target = xhr._cachedTarget[item.id];
40
+ // ensure the cached target
41
+ target || (target = xhr._cachedTarget[item.id] = cachedDocument.getElementById(item.id)),
42
+ // embed the target into the svg
43
+ embed(item.parent, item.svg, target);
44
+ });
45
+ }
46
+ }, // test the ready state change immediately
47
+ xhr.onreadystatechange();
48
+ }
49
+ function svg4everybody(rawopts) {
50
+ function oninterval() {
51
+ // while the index exists in the live <use> collection
52
+ for (// get the cached <use> index
53
+ var index = 0; index < uses.length; ) {
54
+ // get the current <use>
55
+ var use = uses[index], parent = use.parentNode, svg = getSVGAncestor(parent);
56
+ if (svg) {
57
+ var src = use.getAttribute("xlink:href") || use.getAttribute("href");
58
+ // if running with legacy support
59
+ if (nosvg) {
60
+ // create a new fallback image
61
+ var img = document.createElement("img");
62
+ // force display in older IE
63
+ img.style.cssText = "display:inline-block;height:100%;width:100%", // set the fallback size using the svg size
64
+ img.setAttribute("width", svg.getAttribute("width") || svg.clientWidth), img.setAttribute("height", svg.getAttribute("height") || svg.clientHeight),
65
+ // set the fallback src
66
+ img.src = fallback(src, svg, use), // replace the <use> with the fallback image
67
+ parent.replaceChild(img, use);
68
+ } else {
69
+ if (polyfill && (!opts.validate || opts.validate(src, svg, use))) {
70
+ // remove the <use> element
71
+ parent.removeChild(use);
72
+ // parse the src and get the url and id
73
+ var srcSplit = src.split("#"), url = srcSplit.shift(), id = srcSplit.join("#");
74
+ // if the link is external
75
+ if (url.length) {
76
+ // get the cached xhr request
77
+ var xhr = requests[url];
78
+ // ensure the xhr request exists
79
+ xhr || (xhr = requests[url] = new XMLHttpRequest(), xhr.open("GET", url), xhr.send(),
80
+ xhr._embeds = []), // add the svg and id as an item to the xhr embeds list
81
+ xhr._embeds.push({
82
+ parent: parent,
83
+ svg: svg,
84
+ id: id
85
+ }), // prepare the xhr ready state change event
86
+ loadreadystatechange(xhr);
87
+ } else {
88
+ // embed the local id into the svg
89
+ embed(parent, document.getElementById(id));
90
+ }
91
+ }
92
+ }
93
+ } else {
94
+ // increase the index when the previous value was not "valid"
95
+ ++index;
96
+ }
97
+ }
98
+ // continue the interval
99
+ requestAnimationFrame(oninterval, 67);
100
+ }
101
+ var nosvg, fallback, opts = Object(rawopts);
102
+ // configure the fallback method
103
+ fallback = opts.fallback || function(src) {
104
+ return src.replace(/\?[^#]+/, "").replace("#", ".").replace(/^\./, "") + ".png" + (/\?[^#]+/.exec(src) || [ "" ])[0];
105
+ }, // set whether to shiv <svg> and <use> elements and use image fallbacks
106
+ nosvg = "nosvg" in opts ? opts.nosvg : /\bMSIE [1-8]\b/.test(navigator.userAgent),
107
+ // conditionally shiv <svg> and <use>
108
+ nosvg && (document.createElement("svg"), document.createElement("use"));
109
+ // set whether the polyfill will be activated or not
110
+ var polyfill, olderIEUA = /\bMSIE [1-8]\.0\b/, newerIEUA = /\bTrident\/[567]\b|\bMSIE (?:9|10)\.0\b/, webkitUA = /\bAppleWebKit\/(\d+)\b/, olderEdgeUA = /\bEdge\/12\.(\d+)\b/;
111
+ polyfill = "polyfill" in opts ? opts.polyfill : olderIEUA.test(navigator.userAgent) || newerIEUA.test(navigator.userAgent) || (navigator.userAgent.match(olderEdgeUA) || [])[1] < 10547 || (navigator.userAgent.match(webkitUA) || [])[1] < 537;
112
+ // create xhr requests object
113
+ var requests = {}, requestAnimationFrame = window.requestAnimationFrame || setTimeout, uses = document.getElementsByTagName("use");
114
+ // conditionally start the interval if the polyfill is active
115
+ polyfill && oninterval();
116
+ }
117
+ function getSVGAncestor(node) {
118
+ for (var svg = node; "svg" !== svg.nodeName.toLowerCase() && (svg = svg.parentNode); ) {}
119
+ return svg;
120
+ }
121
+ return svg4everybody;
122
+ });
@@ -0,0 +1,104 @@
1
+ import 'bobjoll/ts/library/common';
2
+ import { delegate, delegateRemove, qq } from 'bobjoll/ts/library/dom';
3
+ import { IsUrlValid } from 'bobjoll/ts/library/helpers';
4
+
5
+ class Accordion {
6
+ private static instance: Accordion | null;
7
+
8
+ constructor() {
9
+ if (!Accordion.instance) {
10
+ Accordion.instance = this;
11
+ this.setup();
12
+ }
13
+
14
+ return Accordion.instance;
15
+ }
16
+
17
+ private static addEventListeners() {
18
+ delegate('.accordion__link', 'click', Accordion.eventClickHandler);
19
+ }
20
+
21
+ private static setupMobileNavigation() {
22
+ qq('.accordion:not(.accordion--ready)').forEach(element => {
23
+ const options = qq('.accordion__container > a, .accordion__container > button', element).reduce((acc, link, index) => {
24
+ if (!('' == element.dataset.disableMobileSelect)) {
25
+ link.classList.add('hide-tablet');
26
+ }
27
+ acc += `<option value="${index}">${link.innerHTML}</option>`;
28
+ return acc;
29
+ }, '');
30
+
31
+ if (!('' == element.dataset.disableMobileSelect)) {
32
+ element.insertAdjacentHTML(
33
+ 'afterbegin',
34
+ `
35
+ <li class="accordion__mobile-nav show-tablet">
36
+ <div class="accordion__select__wrapper">
37
+ <select class="accordion__select">${options}</select>
38
+ </div>
39
+ </li>
40
+ `,
41
+ );
42
+ }
43
+
44
+ element.classList.add('accordion--ready');
45
+ });
46
+
47
+ qq('.accordion__select').forEach(select => select.addEventListener('change', Accordion.eventSelectChangeHandler));
48
+ }
49
+
50
+ private static eventClickHandler(this: HTMLElement) {
51
+ const wrapper = <HTMLElement | undefined>this.parent('.accordion');
52
+
53
+ if (wrapper) {
54
+ const collapsible = 'true' === (wrapper.dataset.collapsible || wrapper.dataset.closable);
55
+
56
+ qq('.accordion__link', wrapper).forEach(item => (item !== this ? item.classList.remove('active') : null));
57
+
58
+ (wrapper.classList as any)[collapsible ? 'toggle' : 'add']('active');
59
+ (this.classList as any)[collapsible ? 'toggle' : 'add']('active');
60
+ }
61
+ }
62
+
63
+ private static eventSelectChangeHandler(this: HTMLSelectElement) {
64
+ const indexActive = parseFloat(this.value);
65
+ const wrapper = this.parent('.accordion');
66
+
67
+ if (wrapper && 'number' === typeof indexActive) {
68
+ qq('.accordion__container > a, .accordion__container > button', wrapper as HTMLElement).forEach((button, index) => {
69
+ const href = button.getAttribute('href');
70
+
71
+ if (button.classList.contains('accordion__link')) {
72
+ if (indexActive === index) {
73
+ button.click();
74
+ }
75
+
76
+ button.classList[indexActive === index ? 'add' : 'remove']('active');
77
+ } else if (href && indexActive === index && IsUrlValid(href)) {
78
+ window.location.href = href;
79
+ }
80
+ });
81
+ }
82
+ }
83
+
84
+ public refresh() {
85
+ Accordion.setupMobileNavigation();
86
+ }
87
+
88
+ public destroy() {
89
+ qq('.accordion__select').forEach(select => select.removeEventListener('change', Accordion.eventSelectChangeHandler));
90
+
91
+ delegateRemove('.accordion__link', 'click', Accordion.addEventListeners);
92
+
93
+ Accordion.instance = null;
94
+ }
95
+
96
+ private setup() {
97
+ Accordion.setupMobileNavigation();
98
+ Accordion.addEventListeners();
99
+ }
100
+ }
101
+
102
+ const accordion = new Accordion();
103
+
104
+ export default accordion;
@@ -0,0 +1,27 @@
1
+ import { delegate, qq } from "../library/delegate";
2
+
3
+ const getActiveIndex = (parent: HTMLElement): number => {
4
+ return qq('header li', parent).reduce((acc, link, index) =>
5
+ ((acc = link.classList.contains('active') ? index : acc), acc),
6
+ 0
7
+ );
8
+ }
9
+
10
+ const showActiveTab = (parent: HTMLElement) => {
11
+ const index = getActiveIndex(parent);
12
+ qq('header ~ .content', parent).forEach((element, i) =>
13
+ element.classList[i === index ? 'add' : 'remove']('show')
14
+ );
15
+ window.dispatchEvent(new Event('resize'));
16
+ }
17
+
18
+ const handlerHeaderLink = function(this: HTMLElement) {
19
+ const accordionTabElement = this.parent('.accordion-tabs') as HTMLElement|undefined;
20
+ if (accordionTabElement) {
21
+ qq('header li.active', accordionTabElement).forEach(element => element.classList.remove('active'));
22
+ this.classList.add('active');
23
+ showActiveTab(accordionTabElement);
24
+ }
25
+ }
26
+
27
+ delegate('.accordion-tabs header li', 'click', handlerHeaderLink);
@@ -0,0 +1,51 @@
1
+ // tslint:disable-next-line:import-name
2
+ import Notification, { Position } from 'BobjollNotifications';
3
+ import View from 'BobjollView';
4
+
5
+ const EXT = View.ext;
6
+ const extend = require('bobjoll/ts/library/extend');
7
+
8
+ type AlertType = 'success' | 'warning' | 'error';
9
+
10
+ export interface InsertSettings {
11
+ fixed?: boolean;
12
+ class: AlertType;
13
+ html: string;
14
+ }
15
+
16
+ export interface DefaultSettings {
17
+ fixed: boolean;
18
+ recurrent: boolean;
19
+ timeout: number;
20
+ template: Function;
21
+ position: keyof Position;
22
+ }
23
+
24
+ export interface Settings {
25
+ fixed?: boolean;
26
+ timeout?: number;
27
+ template?: Function;
28
+ position?: keyof Position;
29
+ }
30
+
31
+ export default class Alert extends Notification {
32
+ constructor(settings?: Settings) {
33
+ const defaultSettings = {
34
+ recurrent: false,
35
+ fixed: false,
36
+ timeout: 5000,
37
+ template: require(`BobjollTemplate/alert-v1.0/element.${EXT}`),
38
+ position: 'top-right'
39
+ };
40
+
41
+ super(extend(defaultSettings, settings));
42
+ }
43
+
44
+ public new(html: string, type: AlertType, fixed = false) {
45
+ return super.insert({
46
+ fixed: fixed,
47
+ html: html,
48
+ class: `notification--${type}`
49
+ });
50
+ }
51
+ }
@@ -0,0 +1,17 @@
1
+ export default function(text: string) {
2
+ const textarea = document.createElement('textarea');
3
+
4
+ textarea.textContent = text;
5
+ textarea.style.position = 'fixed';
6
+
7
+ document.body.appendChild(textarea);
8
+
9
+ try {
10
+ textarea.select();
11
+ document.execCommand('copy');
12
+ } catch(err) {
13
+ console.warn('Copy to clipboard failed.', err);
14
+ } finally {
15
+ document.body.removeChild(textarea);
16
+ }
17
+ }
@@ -0,0 +1,119 @@
1
+ import { q } from 'bobjoll/ts/library/gr/dom.q';
2
+
3
+ export default class Countdown {
4
+ private static readonly outer: Function = require('BobjollTemplate/countdown-v1.0/countdown.hbs');
5
+ private static readonly inner: Function = require('BobjollTemplate/countdown-v1.0/countdown-inner.hbs');
6
+
7
+ private element: HTMLElement;
8
+ private settings: CountdownSettings;
9
+ private interval: any;
10
+
11
+ constructor(settings: CountdownSettings) {
12
+ this.settings = settings;
13
+
14
+ if (this.settings.container.classList.contains('countdown')) {
15
+ this.element = this.settings.container;
16
+ } else {
17
+ this.settings.container.insertAdjacentHTML('beforeend', Countdown.outer());
18
+
19
+ this.element = q('.countdown', this.settings.container) || document.createElement('div');
20
+ }
21
+
22
+ this.setup();
23
+ }
24
+
25
+ private static normalize(number: number) {
26
+ if (number < 10) {
27
+ return '0' + number;
28
+ }
29
+
30
+ return number;
31
+ }
32
+
33
+ private static count(options: CountdownSettings) {
34
+ const _distance = options.dateEnd.getTime() - new Date().getTime();
35
+ const _second = 1000;
36
+ const _minute = _second * 60;
37
+ const _hour = _minute * 60;
38
+ const _day = _hour * 24;
39
+
40
+ let date: {
41
+ Days?: number | string;
42
+ Hours?: number | string;
43
+ Mins?: number | string;
44
+ Secs?: number | string;
45
+ } = {};
46
+ let days = Countdown.normalize(Math.floor(_distance / _day));
47
+
48
+ if (days > 0) {
49
+ date.Days = days;
50
+ }
51
+
52
+ date.Hours = Countdown.normalize(Math.floor((_distance % _day) / _hour));
53
+ date.Mins = Countdown.normalize(Math.floor((_distance % _hour) / _minute));
54
+ date.Secs = Countdown.normalize(Math.floor((_distance % _minute) / _second));
55
+
56
+ if (_distance < 0) return;
57
+
58
+ return date;
59
+ }
60
+
61
+ private static countLeft(options: CountdownSettings) {
62
+ if (!options.dateStart || !options.total) return;
63
+
64
+ const dateCurrent = new Date();
65
+ const timeTotal = options.dateEnd.getTime() - options.dateStart.getTime();
66
+ const timeLeft = options.dateEnd.getTime() - dateCurrent.getTime();
67
+ const total = Math.round(Math.abs((timeLeft / timeTotal) * options.total))
68
+ .toString()
69
+ .split('');
70
+
71
+ if (timeLeft <= 0 || !(dateCurrent >= options.dateStart && dateCurrent <= options.dateEnd)) {
72
+ return '0000'.toString().split('');
73
+ }
74
+
75
+ if (total.length < options.total.toString().length) {
76
+ const loop = options.total.toString().length - total.length;
77
+ for (let i = 0; i < loop; i++) {
78
+ total.unshift('0');
79
+ }
80
+ }
81
+
82
+ return total;
83
+ }
84
+
85
+ private setup() {
86
+ let method: Function = Countdown.count;
87
+
88
+ if (this.settings.dateStart && this.settings.total) {
89
+ method = Countdown.countLeft;
90
+ }
91
+
92
+ this.element.innerHTML = Countdown.inner({
93
+ date: method(this.settings),
94
+ });
95
+
96
+ this.interval = setInterval(() => {
97
+ const date = method(this.settings);
98
+
99
+ let data: any = {};
100
+
101
+ if (date) {
102
+ data['date'] = date;
103
+ }
104
+
105
+ this.element.innerHTML = Countdown.inner(data);
106
+
107
+ if (new Date().getTime() >= this.settings.dateEnd.getTime()) {
108
+ clearInterval(this.interval);
109
+ }
110
+ }, 1000);
111
+ }
112
+ }
113
+
114
+ export interface CountdownSettings {
115
+ container: HTMLElement;
116
+ dateStart?: Date;
117
+ dateEnd: Date;
118
+ total?: number;
119
+ }