diff options
Diffstat (limited to 'public/javascripts')
-rw-r--r-- | public/javascripts/analytics.js | 35 | ||||
-rw-r--r-- | public/javascripts/autocomplete.js | 87 | ||||
-rw-r--r-- | public/javascripts/browserFixToolkit.js | 12 | ||||
-rw-r--r-- | public/javascripts/bundle.js | 1 | ||||
-rw-r--r-- | public/javascripts/favorite.js | 79 | ||||
-rw-r--r-- | public/javascripts/featureDetect.js | 29 | ||||
-rw-r--r-- | public/javascripts/frontpage.js | 23 | ||||
-rw-r--r-- | public/javascripts/main.bak.js | 244 | ||||
-rw-r--r-- | public/javascripts/main.js | 45 | ||||
-rw-r--r-- | public/javascripts/schedule.js | 77 | ||||
-rw-r--r-- | public/javascripts/scrollSnap.js | 55 | ||||
-rw-r--r-- | public/javascripts/search.js | 88 | ||||
-rw-r--r-- | public/javascripts/weekSelector.js | 99 |
13 files changed, 0 insertions, 874 deletions
diff --git a/public/javascripts/analytics.js b/public/javascripts/analytics.js deleted file mode 100644 index a93c8a4..0000000 --- a/public/javascripts/analytics.js +++ /dev/null @@ -1,35 +0,0 @@ -/* global ga */ - -const self = {} - -self.send = {} - -self.send.search = function (selectedUser, favorite) { - const hitType = 'event' - - const eventCategory = favorite ? 'search fav' : 'search' - - let eventAction - switch (selectedUser.type) { - case 'c': - eventAction = 'Class' - break - case 't': - eventAction = 'Teacher' - break - case 'r': - eventAction = 'Room' - break - case 's': - eventAction = 'Student' - break - } - - const eventLabel = selectedUser.value - - ga(function () { - ga('send', { hitType, eventCategory, eventAction, eventLabel }) - }) -} - -module.exports = self diff --git a/public/javascripts/autocomplete.js b/public/javascripts/autocomplete.js deleted file mode 100644 index 71fb17d..0000000 --- a/public/javascripts/autocomplete.js +++ /dev/null @@ -1,87 +0,0 @@ -const EventEmitter = require('events') - -const self = new EventEmitter() - -self._items = [] -self._selectedItemIndex = -1 - -self._nodes = { - search: document.querySelector('#search'), - input: document.querySelector('input[type="search"]'), - autocomplete: document.querySelector('.autocomplete') -} - -self.getSelectedItem = function () { - if (self.getItems() === []) return - - if (self.getSelectedItemIndex() === -1) { - return self.getItems()[0] - } else { - return self.getItems()[self.getSelectedItemIndex()] - } -} - -self.getSelectedItemIndex = function () { - return self._selectedItemIndex -} - -self.getItems = function () { - return self._items -} - -self.removeAllItems = function () { - while (self._nodes.autocomplete.firstChild) { - self._nodes.autocomplete.removeChild(self._nodes.autocomplete.firstChild) - } - self._items = [] - self._selectedItemIndex = -1 -} - -self.addItem = function (item) { - const listItem = document.createElement('li') - listItem.textContent = item.value - self._nodes.autocomplete.appendChild(listItem) - self._items.push(item) -} - -self._moveSelected = function (shift) { - if (self._selectedItemIndex + shift >= self.getItems().length) { - self._selectedItemIndex = -1 - } else if (self._selectedItemIndex + shift < -1) { - self._selectedItemIndex = self.getItems().length - 1 - } else { - self._selectedItemIndex += shift - } - - for (let i = 0; i < self.getItems().length; i++) { - self._nodes.autocomplete.children[i].classList.remove('selected') - } - if (self._selectedItemIndex >= 0) { - self._nodes.autocomplete - .children[self._selectedItemIndex].classList.add('selected') - } -} - -self._handleItemClick = function (event) { - if (!self._nodes.autocomplete.contains(event.target)) return - const itemIndex = Array.prototype.indexOf - .call(self._nodes.autocomplete.children, event.target) - self._selectedItemIndex = itemIndex - self.emit('select', self.getSelectedItem()) -} - -self._handleKeydown = function (event) { - if (event.key === 'ArrowDown' || event.key === 'ArrowUp') { - event.preventDefault() - if (event.key === 'ArrowDown') { - self._moveSelected(1) - } else if (event.key === 'ArrowUp') { - self._moveSelected(-1) - } - } -} - -self._nodes.autocomplete.addEventListener('click', self._handleItemClick) -self._nodes.input.addEventListener('keydown', self._handleKeydown) - -module.exports = self diff --git a/public/javascripts/browserFixToolkit.js b/public/javascripts/browserFixToolkit.js deleted file mode 100644 index fbeab74..0000000 --- a/public/javascripts/browserFixToolkit.js +++ /dev/null @@ -1,12 +0,0 @@ -const self = {} - -self.isIE = navigator.userAgent.indexOf('MSIE') !== -1 || - navigator.appVersion.indexOf('Trident/') > 0 - -if (self.isIE) { - self.inputEvent = 'textinput' -} else { - self.inputEvent = 'input' -} - -module.exports = self diff --git a/public/javascripts/bundle.js b/public/javascripts/bundle.js deleted file mode 100644 index 7c1c2ee..0000000 --- a/public/javascripts/bundle.js +++ /dev/null @@ -1 +0,0 @@ -(function e(t,n,r){function o(i,l){if(!n[i]){if(!t[i]){var c="function"==typeof require&&require;if(!l&&c)return c(i,!0);if(s)return s(i,!0);var u=Error("Cannot find module '"+i+"'");throw u.code="MODULE_NOT_FOUND",u}var a=n[i]={exports:{}};t[i][0].call(a.exports,function(e){var n=t[i][1][e];return o(n?n:e)},a,a.exports,e,t,n,r)}return n[i].exports}for(var s="function"==typeof require&&require,i=0;r.length>i;i++)o(r[i]);return o})({1:[function(e,t){function n(){this._events=this._events||{},this._maxListeners=this._maxListeners||void 0}function r(e){return"function"==typeof e}function o(e){return"number"==typeof e}function s(e){return"object"==typeof e&&null!==e}function i(e){return void 0===e}t.exports=n,n.EventEmitter=n,n.prototype._events=void 0,n.prototype._maxListeners=void 0,n.defaultMaxListeners=10,n.prototype.setMaxListeners=function(e){if(!o(e)||0>e||isNaN(e))throw TypeError("n must be a positive number");return this._maxListeners=e,this},n.prototype.emit=function(e){var t,n,o,l,c,u;if(this._events||(this._events={}),"error"===e&&(!this._events.error||s(this._events.error)&&!this._events.error.length)){if(t=arguments[1],t instanceof Error)throw t;var a=Error('Uncaught, unspecified "error" event. ('+t+")");throw a.context=t,a}if(n=this._events[e],i(n))return!1;if(r(n))switch(arguments.length){case 1:n.call(this);break;case 2:n.call(this,arguments[1]);break;case 3:n.call(this,arguments[1],arguments[2]);break;default:l=Array.prototype.slice.call(arguments,1),n.apply(this,l)}else if(s(n))for(l=Array.prototype.slice.call(arguments,1),u=n.slice(),o=u.length,c=0;o>c;c++)u[c].apply(this,l);return!0},n.prototype.addListener=function(e,t){var o;if(!r(t))throw TypeError("listener must be a function");return this._events||(this._events={}),this._events.newListener&&this.emit("newListener",e,r(t.listener)?t.listener:t),this._events[e]?s(this._events[e])?this._events[e].push(t):this._events[e]=[this._events[e],t]:this._events[e]=t,s(this._events[e])&&!this._events[e].warned&&(o=i(this._maxListeners)?n.defaultMaxListeners:this._maxListeners,o&&o>0&&this._events[e].length>o&&(this._events[e].warned=!0,console.error("(node) warning: possible EventEmitter memory leak detected. %d listeners added. Use emitter.setMaxListeners() to increase limit.",this._events[e].length),"function"==typeof console.trace&&console.trace())),this},n.prototype.on=n.prototype.addListener,n.prototype.once=function(e,t){function n(){this.removeListener(e,n),o||(o=!0,t.apply(this,arguments))}if(!r(t))throw TypeError("listener must be a function");var o=!1;return n.listener=t,this.on(e,n),this},n.prototype.removeListener=function(e,t){var n,o,i,l;if(!r(t))throw TypeError("listener must be a function");if(!this._events||!this._events[e])return this;if(n=this._events[e],i=n.length,o=-1,n===t||r(n.listener)&&n.listener===t)delete this._events[e],this._events.removeListener&&this.emit("removeListener",e,t);else if(s(n)){for(l=i;l-->0;)if(n[l]===t||n[l].listener&&n[l].listener===t){o=l;break}if(0>o)return this;1===n.length?(n.length=0,delete this._events[e]):n.splice(o,1),this._events.removeListener&&this.emit("removeListener",e,t)}return this},n.prototype.removeAllListeners=function(e){var t,n;if(!this._events)return this;if(!this._events.removeListener)return 0===arguments.length?this._events={}:this._events[e]&&delete this._events[e],this;if(0===arguments.length){for(t in this._events)"removeListener"!==t&&this.removeAllListeners(t);return this.removeAllListeners("removeListener"),this._events={},this}if(n=this._events[e],r(n))this.removeListener(e,n);else if(n)for(;n.length;)this.removeListener(e,n[n.length-1]);return delete this._events[e],this},n.prototype.listeners=function(e){var t;return t=this._events&&this._events[e]?r(this._events[e])?[this._events[e]]:this._events[e].slice():[]},n.prototype.listenerCount=function(e){if(this._events){var t=this._events[e];if(r(t))return 1;if(t)return t.length}return 0},n.listenerCount=function(e,t){return e.listenerCount(t)}},{}],2:[function(e,t,n){(function(){var e=this,r={};n!==void 0?t.exports=r:e.fuzzy=r,r.simpleFilter=function(e,t){return t.filter(function(t){return r.test(e,t)})},r.test=function(e,t){return null!==r.match(e,t)},r.match=function(e,t,n){n=n||{};var r,o=0,s=[],i=t.length,l=0,c=0,u=n.pre||"",a=n.post||"",d=n.caseSensitive&&t||t.toLowerCase();e=n.caseSensitive&&e||e.toLowerCase();for(var h=0;i>h;h++)r=t[h],d[h]===e[o]?(r=u+r+a,o+=1,c+=1+c):c=0,l+=c,s[s.length]=r;return o===e.length?(l=d===e?1/0:l,{rendered:s.join(""),score:l}):null},r.filter=function(e,t,n){return t&&0!==t.length?"string"!=typeof e?t:(n=n||{},t.reduce(function(t,o,s){var i=o;n.extract&&(i=n.extract(o));var l=r.match(e,i,n);return null!=l&&(t[t.length]={string:l.rendered,score:l.score,index:s,original:o}),t},[]).sort(function(e,t){var n=t.score-e.score;return n?n:e.index-t.index})):[]}})()},{}],3:[function(e,t){"use strict";function n(e,t,n){if(e+="",t-=e.length,0>=t)return e;if(n||0===n||(n=" "),n+=""," "===n&&10>t)return r[t]+e;for(var o="";;){if(1&t&&(o+=n),t>>=1,!t)break;n+=n}return o+e}t.exports=n;var r=[""," "," "," "," "," "," "," "," "," "]},{}],4:[function(e,t,n){(function(e,r,o){"use strict";function s(){function t(e,t){this.scrollLeft=e,this.scrollTop=t}function n(e){return.5*(1-Math.cos(Math.PI*e))}function s(e){if("object"!=typeof e||null===e||e.behavior===o||"auto"===e.behavior||"instant"===e.behavior)return!0;if("object"==typeof e&&"smooth"===e.behavior)return!1;throw new TypeError("behavior not valid")}function i(t){var n,o,s;do t=t.parentNode,n=t===r.body,o=t.clientHeight<t.scrollHeight||t.clientWidth<t.scrollWidth,s="visible"===e.getComputedStyle(t,null).overflow;while(!n&&(!o||s));return n=o=s=null,t}function l(t){var r,o,s,i=h(),c=(i-t.startTime)/a;c=c>1?1:c,r=n(c),o=t.startX+(t.x-t.startX)*r,s=t.startY+(t.y-t.startY)*r,t.method.call(t.scrollable,o,s),(o!==t.x||s!==t.y)&&e.requestAnimationFrame(l.bind(e,t))}function c(n,o,s){var i,c,u,a,f=h();n===r.body?(i=e,c=e.scrollX||e.pageXOffset,u=e.scrollY||e.pageYOffset,a=d.scroll):(i=n,c=n.scrollLeft,u=n.scrollTop,a=t),l({scrollable:i,method:a,startTime:f,startX:c,startY:u,x:o,y:s})}if(!("scrollBehavior"in r.documentElement.style)){var u=e.HTMLElement||e.Element,a=468,d={scroll:e.scroll||e.scrollTo,scrollBy:e.scrollBy,elScroll:u.prototype.scroll||t,scrollIntoView:u.prototype.scrollIntoView},h=e.performance&&e.performance.now?e.performance.now.bind(e.performance):Date.now;e.scroll=e.scrollTo=function(){return s(arguments[0])?(d.scroll.call(e,arguments[0].left||arguments[0],arguments[0].top||arguments[1]),o):(c.call(e,r.body,~~arguments[0].left,~~arguments[0].top),o)},e.scrollBy=function(){return s(arguments[0])?(d.scrollBy.call(e,arguments[0].left||arguments[0],arguments[0].top||arguments[1]),o):(c.call(e,r.body,~~arguments[0].left+(e.scrollX||e.pageXOffset),~~arguments[0].top+(e.scrollY||e.pageYOffset)),o)},u.prototype.scroll=u.prototype.scrollTo=function(){if(s(arguments[0]))return d.elScroll.call(this,arguments[0].left||arguments[0],arguments[0].top||arguments[1]),o;var e=arguments[0].left,t=arguments[0].top;c.call(this,this,"number"==typeof e?e:this.scrollLeft,"number"==typeof t?t:this.scrollTop)},u.prototype.scrollBy=function(){var e=arguments[0];"object"==typeof e?this.scroll({left:e.left+this.scrollLeft,top:e.top+this.scrollTop,behavior:e.behavior}):this.scroll(this.scrollLeft+e,this.scrollTop+arguments[1])},u.prototype.scrollIntoView=function(){if(s(arguments[0]))return d.scrollIntoView.call(this,arguments[0]===o?!0:arguments[0]),o;var t=i(this),n=t.getBoundingClientRect(),l=this.getBoundingClientRect();t!==r.body?(c.call(this,t,t.scrollLeft+l.left-n.left,t.scrollTop+l.top-n.top),e.scrollBy({left:n.left,top:n.top,behavior:"smooth"})):e.scrollBy({left:l.left,top:l.top,behavior:"smooth"})}}}"object"==typeof n?t.exports={polyfill:s}:s()})(window,document)},{}],5:[function(e,t){"use strict";var n={};n.send={},n.send.search=function(e,t){var n="event",r=t?"search fav":"search",o=void 0;switch(e.type){case"c":o="Class";break;case"t":o="Teacher";break;case"r":o="Room";break;case"s":o="Student"}var s=e.value;ga(function(){ga("send",{hitType:n,eventCategory:r,eventAction:o,eventLabel:s})})},t.exports=n},{}],6:[function(e,t){"use strict";var n=e("events"),r=new n;r._items=[],r._selectedItemIndex=-1,r._nodes={search:document.querySelector("#search"),input:document.querySelector('input[type="search"]'),autocomplete:document.querySelector(".autocomplete")},r.getSelectedItem=function(){return r.getItems()!==[]?-1===r.getSelectedItemIndex()?r.getItems()[0]:r.getItems()[r.getSelectedItemIndex()]:void 0},r.getSelectedItemIndex=function(){return r._selectedItemIndex},r.getItems=function(){return r._items},r.removeAllItems=function(){for(;r._nodes.autocomplete.firstChild;)r._nodes.autocomplete.removeChild(r._nodes.autocomplete.firstChild);r._items=[],r._selectedItemIndex=-1},r.addItem=function(e){var t=document.createElement("li");t.textContent=e.value,r._nodes.autocomplete.appendChild(t),r._items.push(e)},r._moveSelected=function(e){r._selectedItemIndex+e>=r.getItems().length?r._selectedItemIndex=-1:-1>r._selectedItemIndex+e?r._selectedItemIndex=r.getItems().length-1:r._selectedItemIndex+=e;for(var t=0;r.getItems().length>t;t++)r._nodes.autocomplete.children[t].classList.remove("selected");r._selectedItemIndex>=0&&r._nodes.autocomplete.children[r._selectedItemIndex].classList.add("selected")},r._handleItemClick=function(e){if(r._nodes.autocomplete.contains(e.target)){var t=Array.prototype.indexOf.call(r._nodes.autocomplete.children,e.target);r._selectedItemIndex=t,r.emit("select",r.getSelectedItem())}},r._handleKeydown=function(e){("ArrowDown"===e.key||"ArrowUp"===e.key)&&(e.preventDefault(),"ArrowDown"===e.key?r._moveSelected(1):"ArrowUp"===e.key&&r._moveSelected(-1))},r._nodes.autocomplete.addEventListener("click",r._handleItemClick),r._nodes.input.addEventListener("keydown",r._handleKeydown),t.exports=r},{events:1}],7:[function(e,t){"use strict";var n={};n.isIE=-1!==navigator.userAgent.indexOf("MSIE")||navigator.appVersion.indexOf("Trident/")>0,n.inputEvent=n.isIE?"textinput":"input",t.exports=n},{}],8:[function(e,t){"use strict";var n=e("events"),r=new n;r._nodes={toggle:document.querySelector(".fav")},r.get=function(){try{var e=JSON.parse(window.localStorage.getItem("fav"));if(null==e)return;var t=USERS.filter(function(t){return t.type===e.type&&t.value===e.value})[0];return t}catch(n){return r.delete(),void 0}},r.set=function(e){window.localStorage.setItem("fav",JSON.stringify(e)),r._nodes.innerHTML=""},r.delete=function(){window.localStorage.removeItem("fav")},r.updateDom=function(e){r._nodes.toggle.innerHTML=e?"":""},r.update=function(e){var t=r.get();if(null==t)return r.updateDom(!1),void 0;var n=t.type===e.type&&t.index===e.index;r.updateDom(n)},r.toggle=function(e){var t=r.get(),n=null!=t&&t.type===e.type&&t.index===e.index;n?(r.delete(),r.updateDom(!1)):(r.set(e),r.updateDom(!0))},r._handleClick=function(){r.emit("click")},r._nodes.toggle.addEventListener("click",r._handleClick),t.exports=r},{events:1}],9:[function(e,t){"use strict";var n={};n._nodes={input:document.querySelector('input[type="search"]'),overflowButton:document.querySelector("#overflow-button")},n._shouldCheck=function(){return-1===FLAGS.indexOf("NO_FEATURE_DETECT")},n._redirect=function(){window.location.href="http://www.meetingpointmco.nl/Roosters-AL/doc/"},n.check=function(){n._shouldCheck()&&(window.onerror=n._redirect,n._nodes.input.getClientRects()[0].top!==n._nodes.overflowButton.getClientRects()[0].top&&n._redirect())},t.exports=n},{}],10:[function(e,t){"use strict";var n=e("./browserFixToolkit"),r={};r._nodes={input:document.querySelector('input[type="search"]')},r.isShown=!1,r.show=function(){document.body.classList.add("no-input"),r.isShown=!0},r.hide=function(){document.body.classList.remove("no-input"),r.isShown=!1},r._nodes.input.addEventListener(n.inputEvent,r.hide),t.exports=r},{"./browserFixToolkit":7}],11:[function(e){"use strict";e("./featureDetect").check();var t=e("./frontpage"),n=e("./search"),r=e("./schedule"),o=e("./weekSelector"),s=e("./favorite"),i=e("./scrollSnap"),l=e("./analytics"),c={};window.state=c,window.require=e,t.show(),o.updateCurrentWeek(),i.startListening(),null!=s.get()?(c.selectedItem=s.get(),s.update(c.selectedItem),l.send.search(c.selectedItem,!0),r.viewItem(o.getSelectedWeek(),c.selectedItem)):n.focus(),n.on("search",function(e){c.selectedItem=e,s.update(c.selectedItem),l.send.search(c.selectedItem),r.viewItem(o.getSelectedWeek(),c.selectedItem)}),o.on("weekChanged",function(e){l.send.search(c.selectedItem),r.viewItem(e,c.selectedItem)}),s.on("click",function(){s.toggle(c.selectedItem)}),document.body.style.opacity=1},{"./analytics":5,"./favorite":8,"./featureDetect":9,"./frontpage":10,"./schedule":12,"./scrollSnap":13,"./search":14,"./weekSelector":15}],12:[function(e,t){"use strict";var n=e("events"),r=e("left-pad"),o=e("./search"),s=new n;s._nodes={schedule:document.querySelector("#schedule")},s._parseMeetingpointHTML=function(e){var t=document.createElement("html");t.innerHTML=e;var n=t.querySelector("center");return n},s._handleLoad=function(e){var t=e.target;if(200>t.status||t.status>=400)return s._handleError(e),void 0;var n=s._parseMeetingpointHTML(t.response);s._removeChilds(),s._nodes.schedule.appendChild(n),s._nodes.schedule.classList.remove("error"),s.emit("load")},s._handleError=function(e){var t=e.target,n=void 0;n=404===t.status?"Sorry, er is (nog) geen rooster voor deze week.":"Sorry, er is iets mis gegaan tijdens het laden van deze week.",s._removeChilds(),s._nodes.schedule.textContent=n,s._nodes.schedule.classList.add("error"),s.emit("load")},s._getURLOfUsers=function(e,t,n){var o=n+1;return"//"+window.location.host+"/meetingpointProxy/Roosters-AL%2Fdoc%2Fdagroosters%2F"+r(e,2,"0")+"%2F"+t+"%2F"+t+r(o,5,"0")+".htm"},s._removeChilds=function(){for(;s._nodes.schedule.firstChild;)s._nodes.schedule.removeChild(s._nodes.schedule.firstChild)},s.viewItem=function(e,t){if(o.updateDom(t),-1===VALID_WEEK_NUMBERS.indexOf(e))return s._handleError({target:{status:404}}),void 0;var n=s._getURLOfUsers(e,t.type,t.index);s._removeChilds();var r=new window.XMLHttpRequest;r.addEventListener("load",s._handleLoad),r.addEventListener("error",s._handleError),r.open("GET",n,!0),r.send()},t.exports=s},{"./search":14,events:1,"left-pad":3}],13:[function(e,t){"use strict";e("smoothscroll-polyfill").polyfill();var n={},r=e("./schedule");n._nodes={search:document.querySelector("#search"),weekSelector:document.querySelector("#week-selector")},n._timeoutID=null,n._getScrollPosition=function(){return document.documentElement&&document.documentElement.scrollTop||document.body.scrollTop},n._handleDoneScrolling=function(){var e=n._getScrollPosition(),t=n._nodes.weekSelector.clientHeight-n._nodes.search.clientHeight;t>e&&e>0&&window.scroll({top:t,left:0,behavior:"smooth"})},n._handleScroll=function(){null!=n._timeoutID&&window.clearTimeout(n._timeoutID),n._timeoutID=window.setTimeout(n._handleDoneScrolling,500);var e=n._getScrollPosition(),t=n._nodes.weekSelector.clientHeight-n._nodes.search.clientHeight;e>=t?document.body.classList.add("week-selector-not-visible"):document.body.classList.remove("week-selector-not-visible")},n._handleWindowResize=function(){var e=n._nodes.weekSelector.clientHeight-n._nodes.search.clientHeight,t=e-(document.body.clientHeight-window.innerHeight);document.body.style.marginBottom=t>0?t+"px":null},n.startListening=function(){window.addEventListener("scroll",n._handleScroll)},r.on("load",n._handleWindowResize),window.addEventListener("resize",n._handleWindowResize),t.exports=n},{"./schedule":12,"smoothscroll-polyfill":4}],14:[function(e,t){"use strict";var n=e("events"),r=e("fuzzy"),o=e("./autocomplete"),s=e("./browserFixToolkit"),i=new n;i._nodes={search:document.querySelector("#search"),input:document.querySelector('input[type="search"]')},i.submit=function(){var e=o.getSelectedItem();null!=e&&(console.log(e),i._nodes.input.blur(),document.body.classList.remove("week-selector-not-visible"),i.emit("search",e))},i.updateDom=function(e){i._nodes.input.value=e.value,o.removeAllItems(),document.body.classList.remove("no-input"),document.body.classList.add("searched")},i.focus=function(){i._nodes.input.focus()},i._handleSubmit=function(e){e.preventDefault(),i.submit()},i._calculate=function(e){var t=r.filter(e,USERS,{extract:function(e){return e.value}}),n=t.slice(0,7),o=n.map(function(e){return e.original});return o},i._handleTextUpdate=function(){var e=i._calculate(i._nodes.input.value);o.removeAllItems();for(var t=0;e.length>t;t++)o.addItem(e[t])},i._handleFocus=function(){i._nodes.input.select()},i._handleBlur=function(){var e=i._nodes.value;i._nodes.value="",i._nodes.value=e,document.activeElement.blur()},o.on("select",i.submit),i._nodes.search.addEventListener("submit",i._handleSubmit),i._nodes.input.addEventListener("focus",i._handleFocus),i._nodes.input.addEventListener("blur",i._handleBlur),i._nodes.input.addEventListener(s.inputEvent,i._handleTextUpdate),t.exports=i},{"./autocomplete":6,"./browserFixToolkit":7,events:1,fuzzy:2}],15:[function(e,t){"use strict";var n=e("events"),r=new n;r._nodes={prevButton:document.querySelectorAll("#week-selector button")[0],nextButton:document.querySelectorAll("#week-selector button")[1],currentWeekNode:document.querySelector("#week-selector .current"),currentWeekNormalText:document.querySelector("#week-selector .current .no-print"),currentWeekPrintText:document.querySelector("#week-selector .current .print")},r._weekOffset=0,r.getCurrentWeek=function(e){var t=(e.getDay()+6)%7;e.setDate(e.getDate()-t+3);var n=e.valueOf();return e.setMonth(0,1),4!==e.getDay()&&e.setMonth(0,1+(4-e.getDay()+7)%7),1+Math.ceil((n-e)/6048e5)},r.getSelectedWeek=function(){var e=new Date,t=new Date(e.getTime()+1e3*604800*r._weekOffset+864e5);return r.getCurrentWeek(t)},r.updateCurrentWeek=function(){var e=r.getSelectedWeek();r.getCurrentWeek(new Date)!==e?r._nodes.currentWeekNode.classList.add("changed"):r._nodes.currentWeekNode.classList.remove("changed"),r.updateDom(),r.emit("weekChanged",e)},r.updateDom=function(){var e=r.getSelectedWeek(),t=0===(new Date).getDay(),n=null;if(t)switch(r._weekOffset){case 0:n="Aanstaande week";break;case 1:n="Volgende week";break;case-1:n="Afgelopen week"}else switch(r._weekOffset){case 0:n="Huidige week";break;case 1:n="Volgende week";break;case-1:n="Vorige week"}null!=n?(r._nodes.currentWeekNormalText.textContent=n+" • "+e,r._nodes.currentWeekPrintText.textContent="Week "+e):(r._nodes.currentWeekNormalText.textContent="Week "+e,r._nodes.currentWeekPrintText.textContent="Week "+e)},r._handlePrevButtonClick=function(){r._weekOffset-=1,r.updateCurrentWeek()},r._handleNextButtonClick=function(){r._weekOffset+=1,r.updateCurrentWeek()},r._nodes.prevButton.addEventListener("click",r._handlePrevButtonClick),r._nodes.nextButton.addEventListener("click",r._handleNextButtonClick),t.exports=r},{events:1}]},{},[11]);
\ No newline at end of file diff --git a/public/javascripts/favorite.js b/public/javascripts/favorite.js deleted file mode 100644 index 9edc248..0000000 --- a/public/javascripts/favorite.js +++ /dev/null @@ -1,79 +0,0 @@ -/* global USERS */ - -const EventEmitter = require('events') - -const self = new EventEmitter() - -self._nodes = { - toggle: document.querySelector('.fav') -} - -self.get = function () { - try { - const localStorageUser = JSON.parse(window.localStorage.getItem('fav')) - if (localStorageUser == null) return - - const correctedUser = USERS.filter(function (user) { - return user.type === localStorageUser.type && - user.value === localStorageUser.value - })[0] - return correctedUser - } catch (e) { - self.delete() - return - } -} - -self.set = function (user) { - window.localStorage.setItem('fav', JSON.stringify(user)) - self._nodes.innerHTML = '' -} - -self.delete = function () { - window.localStorage.removeItem('fav') -} - -self.updateDom = function (isFavorite) { - if (isFavorite) { - self._nodes.toggle.innerHTML = '' - } else { - self._nodes.toggle.innerHTML = '' - } -} - -self.update = function (selectedUser) { - const currentUser = self.get() - - if (currentUser == null) { - self.updateDom(false) - return - } - - const isEqual = currentUser.type === selectedUser.type && - currentUser.index === selectedUser.index - - self.updateDom(isEqual) -} - -self.toggle = function (selectedUser) { - const currentUser = self.get() - const isEqual = currentUser != null && - currentUser.type === selectedUser.type && - currentUser.index === selectedUser.index - - if (isEqual) { - self.delete() - self.updateDom(false) - } else { - self.set(selectedUser) - self.updateDom(true) - } -} - -self._handleClick = function () { - self.emit('click') -} - -self._nodes.toggle.addEventListener('click', self._handleClick) - -module.exports = self diff --git a/public/javascripts/featureDetect.js b/public/javascripts/featureDetect.js deleted file mode 100644 index 3a072a1..0000000 --- a/public/javascripts/featureDetect.js +++ /dev/null @@ -1,29 +0,0 @@ -/* global FLAGS */ - -const self = {} - -self._nodes = { - input: document.querySelector('input[type="search"]'), - overflowButton: document.querySelector('#overflow-button') -} - -self._shouldCheck = function () { - return FLAGS.indexOf('NO_FEATURE_DETECT') === -1 -} - -self._redirect = function () { - window.location.href = 'http://www.meetingpointmco.nl/Roosters-AL/doc/' -} - -self.check = function () { - if (!self._shouldCheck()) return - - window.onerror = self._redirect - - if (self._nodes.input.getClientRects()[0].top !== - self._nodes.overflowButton.getClientRects()[0].top) { - self._redirect() - } -} - -module.exports = self diff --git a/public/javascripts/frontpage.js b/public/javascripts/frontpage.js deleted file mode 100644 index 17cb539..0000000 --- a/public/javascripts/frontpage.js +++ /dev/null @@ -1,23 +0,0 @@ -const browserFixToolkit = require('./browserFixToolkit') - -const self = {} - -self._nodes = { - input: document.querySelector('input[type="search"]') -} - -self.isShown = false - -self.show = function () { - document.body.classList.add('no-input') - self.isShown = true -} - -self.hide = function () { - document.body.classList.remove('no-input') - self.isShown = false -} - -self._nodes.input.addEventListener(browserFixToolkit.inputEvent, self.hide) - -module.exports = self diff --git a/public/javascripts/main.bak.js b/public/javascripts/main.bak.js deleted file mode 100644 index 895dd3f..0000000 --- a/public/javascripts/main.bak.js +++ /dev/null @@ -1,244 +0,0 @@ -/* global ga FLAGS USERS */ - -require('flexibility') - -const fuzzy = require('fuzzy') -// const getUsers = require('./getUsers') -const getURLOfUser = require('./getURLOfUser') -const removeDiacritics = require('diacritics').remove -const getWeek = require('./getWeek') -const easterEggs = require('./easterEggs') - -const searchNode = document.querySelector('#search') -const inputNode = searchNode.querySelector('input[type="search"]') -const autocompleteNode = document.querySelector('.autocomplete') -const scheduleIframe = document.querySelector('#schedule') -const prevButton = document.querySelectorAll('#week-selector button')[0] -const nextButton = document.querySelectorAll('#week-selector button')[1] -const currentWeekNode = document.querySelector('.current') -const favNode = document.querySelector('.fav') - -if (FLAGS.indexOf('NO_FEATURE_DETECT') === -1) { - if (document.querySelector('#schedule').getClientRects()[0].bottom !== - document.body.getClientRects()[0].bottom) { - window.location = 'http://www.meetingpointmco.nl/Roosters-AL/doc/' - } else { - window.onerror = function () { - window.location = 'http://www.meetingpointmco.nl/Roosters-AL/doc/' - } - } -} else { - console.log('feature detection is OFF') -} - -let selectedResult = -1 -let selectedUser -let results = [] -let offset = 0 - -function getCurrentFav () { - if (!window.localStorage.getItem('fav')) return - const favCode = window.localStorage.getItem('fav').split(':') - const fav = USERS.filter(user => user.type === favCode[0] && user.index === Number(favCode[1])) - return fav[0] -} - -function changeFav (isFav) { - if (!selectedUser) return - if (isFav) { - window.localStorage.setItem('fav', selectedUser.type + ':' + selectedUser.index) - } else { - window.localStorage.removeItem('fav') - } - updateFavNode() -} - -function usersEqual (user1, user2) { - if (user1 == null || user2 == null) return false - return user1.type === user2.type && user1.index === user2.index -} - -function updateFavNode () { - if (usersEqual(getCurrentFav(), selectedUser)) { - favNode.innerHTML = '' - } else { - favNode.innerHTML = '' - } -} - -function updateWeekText () { - if (offset === 0) currentWeekNode.innerHTML = `Week ${getWeek() + offset}` - else currentWeekNode.innerHTML = `<strong>Week ${getWeek() + offset}</strong>` -} - -updateWeekText() - -searchNode.addEventListener('keydown', function (e) { - if ((results.length !== 0) && (e.key === 'ArrowDown' || e.key === 'ArrowUp')) { - e.preventDefault() - - if (document.querySelector('.selected')) document.querySelector('.selected').classList.remove('selected') - - const change = e.key === 'ArrowDown' ? 1 : -1 - selectedResult += change - if (selectedResult < -1) selectedResult = results.length - 1 - else if (selectedResult > results.length - 1) selectedResult = -1 - - if (selectedResult !== -1) autocompleteNode.children[selectedResult].classList.add('selected') - } -}) - -let inputEventStr -if (navigator.userAgent.indexOf('MSIE') !== -1 || - navigator.appVersion.indexOf('Trident/') > 0) { - inputEventStr = 'textinput' // IE 6-11 -} else { - inputEventStr = 'input' // normal browsers -} - -searchNode.addEventListener(inputEventStr, function (e) { - document.body.classList.remove('no-input') - autocompleteNode.innerHTML = '' - if (inputNode.value.trim() === '') return - - selectedResult = -1 - results = fuzzy.filter(removeDiacritics(inputNode.value), USERS, { - extract: function (el) { return removeDiacritics(el.value) } - }).slice(0, 7) - - results.forEach(function (result) { - const resultNode = document.createElement('li') - resultNode.innerHTML = `${result.original.value}` - autocompleteNode.appendChild(resultNode) - }) -}) - -searchNode.addEventListener('submit', submitForm) - -function submitForm (e) { - if (e) e.preventDefault() - if (results.length !== 0) { - const indexInResult = selectedResult === -1 ? 0 : selectedResult - selectedUser = USERS[results[indexInResult].index] - } - if (selectedUser == null) return - - document.body.classList.add('searched') - - updateFavNode() - - inputNode.value = selectedUser.value - autocompleteNode.innerHTML = '' - - inputNode.blur() - - scheduleIframe.src = getURLOfUser(offset, selectedUser.type, selectedUser.index + 1) - - let eventAction - switch (selectedUser.type) { - case 'c': - eventAction = 'Class' - break - case 't': - eventAction = 'Teacher' - break - case 'r': - eventAction = 'Room' - break - case 's': - eventAction = 'Student' - break - } - const eventLabel = selectedUser.value - - ga(function () { - ga('send', { hitType: 'event', eventCategory: 'search', eventAction, eventLabel }) - }) -} - -autocompleteNode.addEventListener('click', function (e) { - if (autocompleteNode.contains(e.target)) { - selectedResult = Array.prototype.indexOf.call(e.target.parentElement.childNodes, e.target) - submitForm() - } -}) - -prevButton.addEventListener('click', function () { - offset-- - updateWeekText() - submitForm() -}) - -nextButton.addEventListener('click', function () { - offset++ - updateWeekText() - submitForm() -}) - -inputNode.addEventListener('click', function () { - inputNode.select() -}) - -window.addEventListener('blur', function () { - // this will removed the selection without drawing focus on it (safari) - // this will removed selection even when focusing an iframe (chrome) - const oldValue = inputNode.value - inputNode.value = '' - inputNode.value = oldValue - - // this will hide the keyboard (iOS safari) - document.activeElement.blur() -}) - -searchNode.addEventListener('blur', function (e) { - autocompleteNode.innerHTML = '' -}) - -favNode.addEventListener('click', function () { - if (usersEqual(getCurrentFav(), selectedUser)) { - changeFav(false) - } else { - changeFav(true) - } -}) - -const currentFav = getCurrentFav() - -if (currentFav) { - selectedUser = currentFav - inputNode.value = selectedUser.value - scheduleIframe.src = getURLOfUser(offset, selectedUser.type, selectedUser.index + 1) - updateFavNode() - - let eventAction - switch (selectedUser.type) { - case 'c': - eventAction = 'Class' - break - case 't': - eventAction = 'Teacher' - break - case 'r': - eventAction = 'Room' - break - case 's': - eventAction = 'Student' - break - } - const eventLabel = selectedUser.value - - ga(function () { - ga('send', { hitType: 'event', eventCategory: 'search fav', eventAction, eventLabel }) - }) -} else if (inputNode.value === '') { - document.body.classList.add('no-input') - inputNode.focus() -} - -if (scheduleIframe.src !== '') { - document.body.classList.add('searched') -} - -document.body.style.opacity = '1' - -window.easterEggs = easterEggs diff --git a/public/javascripts/main.js b/public/javascripts/main.js deleted file mode 100644 index 6f99621..0000000 --- a/public/javascripts/main.js +++ /dev/null @@ -1,45 +0,0 @@ -require('./featureDetect').check() - -const frontpage = require('./frontpage') -const search = require('./search') -const schedule = require('./schedule') -const weekSelector = require('./weekSelector') -const favorite = require('./favorite') -const scrollSnap = require('./scrollSnap') -const analytics = require('./analytics') - -const state = {} - -window.state = state -window.require = require - -frontpage.show() -weekSelector.updateCurrentWeek() -scrollSnap.startListening() - -if (favorite.get() != null) { - state.selectedItem = favorite.get() - favorite.update(state.selectedItem) - analytics.send.search(state.selectedItem, true) - schedule.viewItem(weekSelector.getSelectedWeek(), state.selectedItem) -} else { - search.focus() -} - -search.on('search', function (selectedItem) { - state.selectedItem = selectedItem - favorite.update(state.selectedItem) - analytics.send.search(state.selectedItem) - schedule.viewItem(weekSelector.getSelectedWeek(), state.selectedItem) -}) - -weekSelector.on('weekChanged', function (newWeek) { - analytics.send.search(state.selectedItem) - schedule.viewItem(newWeek, state.selectedItem) -}) - -favorite.on('click', function () { - favorite.toggle(state.selectedItem) -}) - -document.body.style.opacity = 1 diff --git a/public/javascripts/schedule.js b/public/javascripts/schedule.js deleted file mode 100644 index bf87af5..0000000 --- a/public/javascripts/schedule.js +++ /dev/null @@ -1,77 +0,0 @@ -/* global VALID_WEEK_NUMBERS */ - -const EventEmitter = require('events') -const leftPad = require('left-pad') -const search = require('./search') - -const self = new EventEmitter() - -self._nodes = { - schedule: document.querySelector('#schedule') -} - -self._parseMeetingpointHTML = function (htmlStr) { - const html = document.createElement('html') - html.innerHTML = htmlStr - const centerNode = html.querySelector('center') - return centerNode -} - -self._handleLoad = function (event) { - const request = event.target - if (request.status < 200 || request.status >= 400) { - self._handleError(event) - return - } - const document = self._parseMeetingpointHTML(request.response) - self._removeChilds() - self._nodes.schedule.appendChild(document) - self._nodes.schedule.classList.remove('error') - self.emit('load') -} - -self._handleError = function (event) { - const request = event.target - let error - if (request.status === 404) { - error = 'Sorry, er is (nog) geen rooster voor deze week.' - } else { - error = 'Sorry, er is iets mis gegaan tijdens het laden van deze week.' - } - self._removeChilds() - self._nodes.schedule.textContent = error - self._nodes.schedule.classList.add('error') - self.emit('load') -} - -self._getURLOfUsers = function (week, type, index) { - const id = index + 1 - return '//' + window.location.host + '/meetingpointProxy/Roosters-AL%2Fdoc%2Fdagroosters%2F' + - leftPad(week, 2, '0') + '%2F' + type + '%2F' + type + leftPad(id, 5, '0') + '.htm' -} - -self._removeChilds = function () { - while (self._nodes.schedule.firstChild) { - self._nodes.schedule.removeChild(self._nodes.schedule.firstChild) - } -} - -self.viewItem = function (week, selectedUser) { - search.updateDom(selectedUser) - - if (VALID_WEEK_NUMBERS.indexOf(week) === -1) { - self._handleError({ target: { status: 404 } }); - return - } - const url = self._getURLOfUsers(week, selectedUser.type, selectedUser.index) - - self._removeChilds() - - const request = new window.XMLHttpRequest() - request.addEventListener('load', self._handleLoad) - request.addEventListener('error', self._handleError) - request.open('GET', url, true) - request.send() -} - -module.exports = self diff --git a/public/javascripts/scrollSnap.js b/public/javascripts/scrollSnap.js deleted file mode 100644 index 167f0c1..0000000 --- a/public/javascripts/scrollSnap.js +++ /dev/null @@ -1,55 +0,0 @@ -require('smoothscroll-polyfill').polyfill() - -const self = {} -const schedule = require('./schedule') - -self._nodes = { - search: document.querySelector('#search'), - weekSelector: document.querySelector('#week-selector') -} - -self._timeoutID = null - -self._getScrollPosition = function () { - return (document.documentElement && document.documentElement.scrollTop) || - document.body.scrollTop -} - -self._handleDoneScrolling = function () { - const scrollPosition = self._getScrollPosition() - const weekSelectorHeight = self._nodes.weekSelector.clientHeight - self._nodes.search.clientHeight - if (scrollPosition < weekSelectorHeight && scrollPosition > 0) { - window.scroll({ top: weekSelectorHeight, left: 0, behavior: 'smooth' }) - } -} - -self._handleScroll = function () { - if (self._timeoutID != null) window.clearTimeout(self._timeoutID) - self._timeoutID = window.setTimeout(self._handleDoneScrolling, 500) - - const scrollPosition = self._getScrollPosition() - const weekSelectorHeight = self._nodes.weekSelector.clientHeight - self._nodes.search.clientHeight - if (scrollPosition >= weekSelectorHeight) { - document.body.classList.add('week-selector-not-visible') - } else { - document.body.classList.remove('week-selector-not-visible') - } -} - -self._handleWindowResize = function () { - const weekSelectorHeight = self._nodes.weekSelector.clientHeight - self._nodes.search.clientHeight - const extraPixelsNeeded = weekSelectorHeight - (document.body.clientHeight - window.innerHeight) - if (extraPixelsNeeded > 0) { - document.body.style.marginBottom = extraPixelsNeeded + 'px' - } else { - document.body.style.marginBottom = null - } -} - -self.startListening = function () { - window.addEventListener('scroll', self._handleScroll) -} - -schedule.on('load', self._handleWindowResize) -window.addEventListener('resize', self._handleWindowResize) -module.exports = self diff --git a/public/javascripts/search.js b/public/javascripts/search.js deleted file mode 100644 index a07d6dd..0000000 --- a/public/javascripts/search.js +++ /dev/null @@ -1,88 +0,0 @@ -/* global USERS */ - -const EventEmitter = require('events') -const fuzzy = require('fuzzy') -const autocomplete = require('./autocomplete') -const browserFixToolkit = require('./browserFixToolkit') - -const self = new EventEmitter() - -self._nodes = { - search: document.querySelector('#search'), - input: document.querySelector('input[type="search"]') -} - -self.submit = function () { - const selectedItem = autocomplete.getSelectedItem() - if (selectedItem == null) return - - console.log(selectedItem) - - self._nodes.input.blur() - document.body.classList.remove('week-selector-not-visible') // Safari bug - - self.emit('search', selectedItem) -} - -self.updateDom = function (selectedItem) { - self._nodes.input.value = selectedItem.value - autocomplete.removeAllItems() - document.body.classList.remove('no-input') - document.body.classList.add('searched') -} - -self.focus = function () { - self._nodes.input.focus() -} - -self._handleSubmit = function (event) { - event.preventDefault() - self.submit() -} - -self._calculate = function (searchTerm) { - const allResults = fuzzy.filter(searchTerm, USERS, { - extract: function (item) { return item.value } - }) - const firstResults = allResults.slice(0, 7) - - const originalResults = firstResults.map(function (result) { - return result.original - }) - - return originalResults -} - -self._handleTextUpdate = function () { - const results = self._calculate(self._nodes.input.value) - - autocomplete.removeAllItems() - for (let i = 0; i < results.length; i++) { - autocomplete.addItem(results[i]) - } -} - -self._handleFocus = function () { - self._nodes.input.select() -} - -self._handleBlur = function () { - // this will removed the selection without drawing focus on it (safari) - // this will removed selection even when focusing an iframe (chrome) - const oldValue = self._nodes.value - self._nodes.value = '' - self._nodes.value = oldValue - - // this will hide the keyboard (iOS safari) - document.activeElement.blur() -} - -autocomplete.on('select', self.submit) - -self._nodes.search.addEventListener('submit', self._handleSubmit) -self._nodes.input.addEventListener('focus', self._handleFocus) -self._nodes.input.addEventListener('blur', self._handleBlur) -self._nodes.input.addEventListener(browserFixToolkit.inputEvent, - self._handleTextUpdate) - -module.exports = self diff --git a/public/javascripts/weekSelector.js b/public/javascripts/weekSelector.js deleted file mode 100644 index d4e7f2a..0000000 --- a/public/javascripts/weekSelector.js +++ /dev/null @@ -1,99 +0,0 @@ -const EventEmitter = require('events') - -const self = new EventEmitter() - -self._nodes = { - prevButton: document.querySelectorAll('#week-selector button')[0], - nextButton: document.querySelectorAll('#week-selector button')[1], - currentWeekNode: document.querySelector('#week-selector .current'), - currentWeekNormalText: document.querySelector('#week-selector .current .no-print'), - currentWeekPrintText: document.querySelector('#week-selector .current .print') -} - -self._weekOffset = 0 - -// copied from http://www.meetingpointmco.nl/Roosters-AL/doc/dagroosters/untisscripts.js, -// were using the same code as they do to be sure that we always get the same -// week number. -self.getCurrentWeek = function (target) { - const dayNr = (target.getDay() + 6) % 7 - target.setDate(target.getDate() - dayNr + 3) - const firstThursday = target.valueOf() - target.setMonth(0, 1) - if (target.getDay() !== 4) { - target.setMonth(0, 1 + ((4 - target.getDay()) + 7) % 7) - } - - return 1 + Math.ceil((firstThursday - target) / 604800000) -} - -self.getSelectedWeek = function () { - const now = new Date() - const targetDate = new Date(now.getTime() + - self._weekOffset * 604800 * 1000 + 86400 * 1000) - return self.getCurrentWeek(targetDate) -} - -self.updateCurrentWeek = function () { - const selectedWeekNumber = self.getSelectedWeek() - if (self.getCurrentWeek(new Date()) !== selectedWeekNumber) { - self._nodes.currentWeekNode.classList.add('changed') - } else { - self._nodes.currentWeekNode.classList.remove('changed') - } - self.updateDom() - self.emit('weekChanged', selectedWeekNumber) -} - -self.updateDom = function () { - const selectedWeekNumber = self.getSelectedWeek() - const isSunday = new Date().getDay() === 0 - let humanReadableWeek = null - if (isSunday) { - switch (self._weekOffset) { - case 0: - humanReadableWeek = 'Aanstaande week' - break - case 1: - humanReadableWeek = 'Volgende week' - break - case -1: - humanReadableWeek = 'Afgelopen week' - break - } - } else { - switch (self._weekOffset) { - case 0: - humanReadableWeek = 'Huidige week' - break - case 1: - humanReadableWeek = 'Volgende week' - break - case -1: - humanReadableWeek = 'Vorige week' - break - } - } - if (humanReadableWeek != null) { - self._nodes.currentWeekNormalText.textContent = humanReadableWeek + ' • ' + selectedWeekNumber - self._nodes.currentWeekPrintText.textContent = 'Week ' + selectedWeekNumber - } else { - self._nodes.currentWeekNormalText.textContent = 'Week ' + selectedWeekNumber - self._nodes.currentWeekPrintText.textContent = 'Week ' + selectedWeekNumber - } -} - -self._handlePrevButtonClick = function () { - self._weekOffset -= 1 - self.updateCurrentWeek() -} - -self._handleNextButtonClick = function () { - self._weekOffset += 1 - self.updateCurrentWeek() -} - -self._nodes.prevButton.addEventListener('click', self._handlePrevButtonClick) -self._nodes.nextButton.addEventListener('click', self._handleNextButtonClick) - -module.exports = self |