diff --git a/tools/breaklock/.gitignore b/tools/breaklock/.gitignore deleted file mode 100644 index ade14b9..0000000 --- a/tools/breaklock/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -.DS_Store -npm-debug.log -node_modules diff --git a/tools/breaklock/LICENSE b/tools/breaklock/LICENSE deleted file mode 100644 index 1fab0d9..0000000 --- a/tools/breaklock/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2017 maxwellito - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/tools/breaklock/app.css b/tools/breaklock/app.css deleted file mode 100644 index 6fc9ddb..0000000 --- a/tools/breaklock/app.css +++ /dev/null @@ -1 +0,0 @@ -.countdown-container{float:right;width:10rem;height:1rem;margin:.25rem 0 .25rem .5rem;border:1px solid currentColor;border-radius:.25rem;box-sizing:border-box;color:inherit}.alert{color:red;animation:blink 1s infinite;-webkit-animation:blink 1s infinite}.countdown-content{display:block;height:100%;width:0;color:inherit;background-color:currentColor}.status-bar{height:1.5rem;padding-bottom:.25rem;border-bottom:1px dotted #fff}.status-bar:after{content:"";clear:both}.status-bar-cancel{height:100%;font-size:1rem}.status-bar-cancel:before{content:"\AB";margin-right:.25em}.status-bar-info{float:right}.history{height:7rem;overflow-x:scroll;font-size:0;line-height:0;white-space:nowrap;-webkit-backface-visibility:hidden;backface-visibility:hidden}.history-container{display:table-cell;vertical-align:top}.history-container:empty{display:block}.history-container:empty:after{content:attr(data-helper);display:block;line-height:6;font-size:1rem;text-align:center}.history-start-helper{display:none}.history svg{width:4.16667rem;height:5rem;display:inline-block;margin:1rem 0;transform:translateZ(0);animation:expand .25s,fadein .5s linear;-webkit-transform:translateZ(0);-webkit-animation:expand .25s,fadein .5s linear}.history svg.success{border:.125rem solid currentColor;border-radius:.25rem;padding:.375rem .25rem;margin-top:.5rem;margin-bottom:.5rem}.history svg+svg{margin-left:1rem}@keyframes fadein{0%{opacity:0}50%{opacity:0}to{opacity:1}}@-webkit-keyframes fadein{0%{opacity:0}50%{opacity:0}to{opacity:1}}@keyframes expand{0%{width:0;padding:0}}@-webkit-keyframes expand{0%{width:0;padding:0}}@media (min-width:1024px),(orientation:landscape){.history{height:auto;border-bottom:none}.history-container{display:block}.history svg{float:left;margin-right:1rem}.history svg+svg{margin-left:0}}.lock{display:block;width:320px;max-width:90%;margin:auto}.lock-flashdots circle{visibility:hidden;opacity:1}.lock-flashdots circle.active{visibility:visible;opacity:0;transition:visibility 0s,opacity .3s}.summary{position:fixed;top:0;bottom:auto;left:0;right:0;height:0;overflow:hidden;background:#14171b;visibility:hidden;transition:visibility 0s linear .5s,height .5s ease;-webkit-transition:visibility 0s linear .5s,height .5s ease}body.deepblack .summary{background-color:#000}.summary.active{visibility:visible;height:100%;transition:visibility 0s,height .5s ease .5s;-webkit-transition:visibility 0s,height .5s ease .5s}.summary.view{padding:0}.summary.view .view-bloc{padding:2rem}.summary-title{margin:0}.summary-title.success{color:#fff;background-color:#169}.summary-title.fail{color:#fff;background-color:red}.summary-action-button>*{vertical-align:middle;margin-right:.5em}.summary-action-button{display:block;font-size:1.5rem}.summary-share{font-size:2rem;line-height:0}.summary-share-link{display:inline-block;margin-right:.375em}@media (min-width:1024px),(orientation:landscape){.summary{align-items:center}}@media (max-width:1023px){.history-wrap{border-bottom:1px dotted #fff}.history-wrap .history{margin:0 -2rem}.history-wrap .history-container{padding:0 2rem}}@media (min-width:1024px),(orientation:landscape){.game-layout-lock{align-self:center}}.extender-button{display:block;width:100%;padding:.25em;text-align:center;background:hsla(0,0%,100%,.125);border-bottom:1px dotted #fff}.extender-button:after{content:"\25BD";margin-left:.5em}.extender.active .extender-button:after{content:"\25B3"}.extender-content{display:none}.extender.active .extender-content{display:inherit}@media (min-width:1024px),(orientation:landscape){.extender.small-only .extender-button{display:none}.extender.small-only .extender-content{display:inherit}}.selectbox-item{cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none}.selectbox{border:1px solid #fff;margin-bottom:.5rem;border-radius:.4rem;padding:.25rem}.selectbox-item{padding:.25rem;text-align:center;display:inline-block;box-sizing:border-box}.selectbox-item.active{background:#fff;color:#14171b;border-radius:.2rem}.selectbox-1 .selectbox-item{width:100%}.selectbox-2 .selectbox-item{width:50%}.selectbox-3 .selectbox-item{width:33.33%}.selectbox-4 .selectbox-item{width:25%}.selectbox-5 .selectbox-item{width:20%}.selector-left,.selector-right{width:2rem}.selector-left{float:left}.selector-right{float:right}.selector-label{margin:0 auto;display:block}.menu-title{margin:0}.menu-layout-instructions{-webkit-flex:1 0 auto}.menu-layout-form{max-width:480px}.introduction-demo{display:block;width:100%;max-width:320px;margin:1rem auto}@media (min-width:1024px),(orientation:landscape){.menu-layout{align-items:center}}button{cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none}@font-face{font-family:Roboto Mono;font-weight:300;src:url("assets/fonts/robotomono-light-webfont.woff2") format("woff2"),url("assets/fonts/robotomono-light-webfont.woff") format("woff"),url("assets/fonts/robotomono-light-webfont.ttf") format("truetype")}*{font-family:Roboto Mono,Menlo,Consolas,Andale Mono,monospace;font-weight:300;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body,html{height:100%}body{position:relative;margin:0 auto;padding:2rem;max-width:1200px;box-sizing:border-box;background:#14171b;color:#fff;overscroll-behavior-y:none}body.deepblack{background-color:#000}a{color:inherit}button{background:none;color:#fff;line-height:inherit;font-size:inherit;border:none;padding:0}button,h1,h2,h3,h4,h5,h6{font-weight:300}caption,table,tbody,td,tfoot,th,thead,tr{margin:0;padding:0;border:0;font-size:100%;font:inherit;vertical-align:middle}table{border-collapse:collapse;border-spacing:0}.icon{width:1em;height:1em;vertical-align:middle}.icon *{fill:currentColor}::-webkit-scrollbar{display:none}:focus{outline:none}.bloc{border-top:1px dotted currentColor;padding:.75rem 0}.action-btn{display:block;text-align:center;margin:0 auto .25rem;font-size:1.25rem;border:1px solid #fff;padding:.25rem 1rem;box-shadow:.25rem .25rem 0 0 #fff;border-radius:.4rem}.highlight{display:inline-block;padding:0 .5rem;color:#14171b;background-color:#fff}.highlight:after{content:"_";animation:blink 1.2s infinite;-webkit-animation:blink 1.2s infinite}.hide{display:none}.unselectable{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.view{display:flex;flex-direction:column;justify-content:space-between;position:absolute;top:0;bottom:0;left:0;right:0;padding:2rem;overflow:scroll}.view-bloc{flex-grow:1}@media (min-width:1024px),(orientation:landscape){.view{flex-direction:row}.view-bloc{flex-basis:50%;max-height:100%;overflow-y:scroll}.view-bloc+.view-bloc{margin-left:1rem}}@media (max-width:320px){html{font-size:14px}}@media (min-width:1024px){html{font-size:20px}}@keyframes blink{0%{opacity:1}50%{opacity:1}50.01%{opacity:0}to{opacity:0}}@-webkit-keyframes blink{0%{opacity:1}50%{opacity:1}50.01%{opacity:0}to{opacity:0}} \ No newline at end of file diff --git a/tools/breaklock/app.js b/tools/breaklock/app.js deleted file mode 100644 index 1810337..0000000 --- a/tools/breaklock/app.js +++ /dev/null @@ -1 +0,0 @@ -!function(t){function e(i){if(n[i])return n[i].exports;var a=n[i]={i:i,l:!1,exports:{}};return t[i].call(a.exports,a,a.exports,e),a.l=!0,a.exports}var n={};e.m=t,e.c=n,e.i=function(t){return t},e.d=function(t,n,i){e.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:i})},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=9)}([function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var i={create:function(t){var e,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},a=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;if(e=-1===i.SVG_ELEMENTS.indexOf(t)?document.createElement(t):document.createElementNS(i.SVG_NAMESPACE,t),n.constructor===String)e.setAttribute("class",n);else for(var r in n)e.setAttribute(r,n[r]);if(a instanceof Array)for(var o=0;o=0;e--)t.childNodes[e].remove()},SVG_NAMESPACE:"http://www.w3.org/2000/svg",XLINK_NAMESPACE:"http://www.w3.org/1999/xlink",SVG_ELEMENTS:["svg","g","circle","line","path","use","rect"]};e.default=i},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var i={GAME:{DIFFICULTY:{EASY:4,MEDIUM:5,HARD:6},TYPE:{PRACTICE:1,CHALLENGE:2,COUNTDOWN:3},ACTIONS:{CONTINUE:1,NEW_GAME:2,BACK_HOME:3}},SOCIAL:{PLATFORMS:{FB:{NAME:"Facebook",ICON:"facebook",URL:function(t){return"https://www.facebook.com/sharer/sharer.php?u="+encodeURI(t)}},TWITTER:{NAME:"Twitter",ICON:"twitter",URL:function(t,e,n){return"http://twitter.com/"+(t?"share?":"intent/tweet?")+(e?"text="+encodeURI(e)+"&":"")+(t?"url="+encodeURI(t)+"&":"")+(n?"hashtags="+encodeURI(n.join(",")):"")}}},MESSAGE:"I wasted my time on BreakLock, it's pointless, don't try it.",TAGS:["breaklock"]},URL:"https://maxwellito.github.io/breaklock/",COLORS:{BRIGHT:"#ffffff",DARK:"#14171b",SUCCESS:"#116699",ERROR:"#ff0000"},PATTERN:{HEX_COLOR_START:"66",HEX_COLOR_END:"FF"}};e.default=i},function(t,e,n){"use strict";function i(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(e,"__esModule",{value:!0});var a=function(){function t(t,e){for(var n=0;n>0===n&&e%3-n%3==n%3-t%3&&Math.floor(e/3)-Math.floor(n/3)==Math.floor(n/3)-Math.floor(t/3)){var i=this.addDot(n);return this.isComplete()||(this.suite.push(t),i.push(t)),i}return this.suite.push(t),[t]}},{key:"isComplete",value:function(){return this.suite.length>=this.dotLength}},{key:"gotDot",value:function(t){return~this.suite.indexOf(t)}},{key:"compare",value:function(t){for(var e=0,n=0,i=0;i1&&void 0!==arguments[1]?arguments[1]:14,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"#fff",i=[];n=n instanceof Array?n:[n];for(var a=1;a0&&void 0!==arguments[0]?arguments[0]:3,e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=[];e.fill=e.fill||"#fff";for(var i=0;i<9;i++)n.push(o.default.create("circle",{cx:i%3*this.GRID_GUTTER+this.SVG_MARGIN,cy:Math.floor(i/3)*this.GRID_GUTTER+this.SVG_MARGIN,rel:i,r:t}));return this.addGroup(e,n)}},{key:"addGroup",value:function(t,e){var n=o.default.create("g",t,e);return this.el.appendChild(n),n}},{key:"addCombinaison",value:function(t,e,n){var i=t+e+n,a=Math.min(Math.floor(this.SVG_WIDTH/i),this.SVG_COMB_EXP),r=Math.floor(.75*a),s=Math.floor(.25*a),u=r+s,l=Math.floor((this.SVG_WIDTH-(i-1)*u)/2),c=this.SVG_WIDTH+Math.floor(this.SVG_COMB_EXP/2);this.el.setAttribute("viewBox","0 0 "+this.SVG_WIDTH+" "+(this.SVG_WIDTH+this.SVG_COMB_EXP));for(var h=[],d=0;d=0;e--)if(c[e].el===t)return c.splice(e,1)[0]}function r(t){t.nextFrame=u(function(){o(t)})}function o(t){if(t.counter-=1,t.counter<=0)return t.el.textContent=t.originalText,void a(t.el);var e=Math.floor(t.originalLength-t.counter/h);t.el.textContent=t.originalText.substr(0,e)+s(Math.min(t.originalLength-e,3)),r(t)}function s(t){var e="",n="abcdefghijklmnopqrstuvwxyz0123456789 _*%!?/\\|#@";if(t<=0)return e;for(var i=0;i0;a--)n="0"+n;return(i?"-":"")+n};e.default=i},function(t,e,n){"use strict";function i(t){return t&&t.__esModule?t:{default:t}}function a(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(e,"__esModule",{value:!0});var r=function(){function t(t,e){for(var n=0;n0?this.remaining:0,this.el.classList[this.remaining>10?"remove":"add"]("alert"),this.counterEl.textContent=(0,s.default)(this.remaining,3),this.barEl.style.width=this.remaining/this.duration*100+"%",0==this.remaining&&(this.stop(),this.endCallback&&this.endCallback())}}]),t}();e.default=c},function(t,e,n){"use strict";function i(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(e,"__esModule",{value:!0});var a=function(){function t(t,e){for(var n=0;n0&&window.requestAnimationFrame(this.scrollToStart.bind(this))}},{key:"clear",value:function(t){this.lastPattern=null,this.containerEl.remove(),this.containerEl=o.default.create("div",{class:"history-container","data-helper":t}),this.el.appendChild(this.containerEl)}}]),t}();e.default=s},function(t,e,n){"use strict";function i(t){return t&&t.__esModule?t:{default:t}}function a(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(e,"__esModule",{value:!0});var r=function(){function t(t,e){for(var n=0;n=t?a:n,i=r<=e&&o>=e?a:i}var s=void 0;if(void 0!==n&&void 0!=i){var u=3*i+n;s=this.triggerDot(u)}return s||this.updateLine(t,e),!0}}},{key:"triggerDot",value:function(t){var e=this;if(!this.pattern.gotDot(t)){var n=this.pattern.addDot(t);navigator.vibrate&&navigator.vibrate(20),n.forEach(function(t,i){var a=l.default.prototype.GRID_GUTTER*(t%3)+l.default.prototype.SVG_MARGIN,r=l.default.prototype.GRID_GUTTER*Math.floor(t/3)+l.default.prototype.SVG_MARGIN;if(e.closeLine(a,r),e.bigDotsEl.childNodes[t].classList.add("active"),i+1===n.length&&e.pattern.isComplete())return e.checkPattern();e.startLine(a,r)})}}},{key:"reset",value:function(){clearTimeout(this.isPendingReset),this.isPendingReset=null,this.pattern.reset(),this.currentLine=null;for(var t=0;t<9;t++)this.bigDotsEl.childNodes[t].classList.remove("active");for(var e=this.patternEl.childNodes.length-1;e>=0;e--)this.patternEl.childNodes[e].remove();this.patternEl.setAttribute("stroke",f.default.COLORS.BRIGHT)}},{key:"checkPattern",value:function(){var t=this.onNewPattern(this.pattern);return this.isPendingReset=setTimeout(this.reset.bind(this),1e3),this.patternEl.setAttribute("stroke",t?f.default.COLORS.SUCCESS:f.default.COLORS.ERROR),t}},{key:"startLine",value:function(t,e){this.currentLine=h.default.create("line",{x1:t,y1:e}),this.patternEl.appendChild(this.currentLine)}},{key:"updateLine",value:function(t,e){this.currentLine&&(this.currentLine.setAttribute("x2",t),this.currentLine.setAttribute("y2",e))}},{key:"closeLine",value:function(t,e){this.updateLine(t,e),this.currentLine=null}}]),t}();e.default=p},function(t,e,n){"use strict";function i(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(e,"__esModule",{value:!0});var a=function(){function t(t,e){for(var n=0;n"),this.labelEl=o.default.create("span","selectbox-item selector-label"),this.el=o.default.create("div","selector selectbox",[this.btnLeft,this.btnRight,this.labelEl]),this.el}},{key:"init",value:function(){this.btnLeft.addEventListener("click",this.previous.bind(this)),this.btnLeft.addEventListener("touchstart",this.previous.bind(this)),this.btnRight.addEventListener("click",this.next.bind(this)),this.btnRight.addEventListener("touchstart",this.next.bind(this))}},{key:"setChoices",value:function(t){this.choices=t;for(var e=this.choices.length-1;e>=0;e--)this.selectionIndex=this.choices[e].default?e:this.selectionIndex;this.selectionIndex=this.selectionIndex||0,this.updateLabel()}},{key:"updateLabel",value:function(){this.selectionIndex=(this.selectionIndex+this.choices.length)%this.choices.length;var t=this.choices[this.selectionIndex];return this.labelEl.textContent=t.label,this.selectCallback&&this.selectCallback(this.choices[this.selectionIndex]),this.selectionIndex}},{key:"next",value:function(t){return t.preventDefault(),t.stopPropagation(),this.selectionIndex++,this.updateLabel()}},{key:"previous",value:function(t){return t.preventDefault(),t.stopPropagation(),this.selectionIndex--,this.updateLabel()}},{key:"onSelect",value:function(t){this.selectCallback=t,this.updateLabel()}},{key:"getValue",value:function(){var t=this.choices[this.selectionIndex];return t&&t.value}}]),t}();e.default=s},function(t,e,n){"use strict";function i(t){return t&&t.__esModule?t:{default:t}}function a(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(e,"__esModule",{value:!0});var r=function(){function t(t,e){for(var n=0;n=e}).map(function(t){return t.text})):(n="Sorry, you didn't make it this time. ",i=r),n+i[Math.floor(i.length*Math.random())]}Object.defineProperty(e,"__esModule",{value:!0});var a=[{min:1,max:3,text:"That was pure luck, nothing else. Stop dreamin."},{min:2,max:4,text:"You got lucky, without staying up all night."},{min:1,max:2,text:"No merit. Absolutely none."},{min:2,max:5,text:"That was given on a golden plate."},{min:1,max:4,text:"Absolutely no synapse got used during that game."},{min:2,max:5,text:"Don't even dare to tweet your score."},{min:8,max:10,text:"Saperlipopette!! That was close."},{min:4,max:8,text:"Seems legit, with a bit of luck."},{min:7,max:10,text:"Pretty good!"},{min:9,max:10,text:"But you made it!"},{min:11,max:50,text:"Trying random patterns is not a strategy..."},{min:11,max:50,text:"That was looooooooong."},{min:11,max:50,text:"At least you made it."},{min:11,max:50,text:"You must hate this game by now."},{min:11,max:50,text:"I hope you didn't cheat."},{min:41,max:403,text:"Your dedication is impressive."},{min:404,max:404,text:"Logic not found."},{min:405,max:999,text:"No comment."}],r=["I believe there's some work to do.","Do you understand the game? Don't take it personnaly, I struggle to explain it.","One day you will make it...","It's not funny for you, but it is for me ;)","Don't stress, you will make it.","If you want to avoid battles, stay out of the grassy areas!","Even if you loose in battle, if you surpass what you've done before, you have bested yourself.","TILT! Insert coin and try again!"];e.default=i},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var i={greydient:function(t,e){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;t="string"==typeof t?parseInt(t,16):t,e="string"==typeof e?parseInt(e,16):e,t=Math.min(255,Math.max(0,t)),e=Math.min(255,Math.max(0,e)),n++;for(var i=[],a=(e-t)/n,r=0;r<=n;r++){var o=Math.round(t+r*a),s=o.toString(16);i.push("#"+s+s+s)}return i}};e.default=i},function(t,e){},function(t,e){},function(t,e){},function(t,e){},function(t,e){},function(t,e){},function(t,e){},function(t,e){},function(t,e){},function(t,e){}]); \ No newline at end of file diff --git a/tools/breaklock/assets/banner.png b/tools/breaklock/assets/banner.png deleted file mode 100644 index 0642f96..0000000 Binary files a/tools/breaklock/assets/banner.png and /dev/null differ diff --git a/tools/breaklock/assets/favicon.ico b/tools/breaklock/assets/favicon.ico deleted file mode 100644 index 1d258ce..0000000 Binary files a/tools/breaklock/assets/favicon.ico and /dev/null differ diff --git a/tools/breaklock/assets/fonts/robotomono-light-webfont.ttf b/tools/breaklock/assets/fonts/robotomono-light-webfont.ttf deleted file mode 100644 index 430b8d7..0000000 Binary files a/tools/breaklock/assets/fonts/robotomono-light-webfont.ttf and /dev/null differ diff --git a/tools/breaklock/assets/fonts/robotomono-light-webfont.woff b/tools/breaklock/assets/fonts/robotomono-light-webfont.woff deleted file mode 100644 index cfc4403..0000000 Binary files a/tools/breaklock/assets/fonts/robotomono-light-webfont.woff and /dev/null differ diff --git a/tools/breaklock/assets/fonts/robotomono-light-webfont.woff2 b/tools/breaklock/assets/fonts/robotomono-light-webfont.woff2 deleted file mode 100644 index b55b283..0000000 Binary files a/tools/breaklock/assets/fonts/robotomono-light-webfont.woff2 and /dev/null differ diff --git a/tools/breaklock/assets/icons/icon-128x128.png b/tools/breaklock/assets/icons/icon-128x128.png deleted file mode 100644 index 85e9ab0..0000000 Binary files a/tools/breaklock/assets/icons/icon-128x128.png and /dev/null differ diff --git a/tools/breaklock/assets/icons/icon-144x144.png b/tools/breaklock/assets/icons/icon-144x144.png deleted file mode 100644 index 0c81288..0000000 Binary files a/tools/breaklock/assets/icons/icon-144x144.png and /dev/null differ diff --git a/tools/breaklock/assets/icons/icon-152x152.png b/tools/breaklock/assets/icons/icon-152x152.png deleted file mode 100644 index bf6b960..0000000 Binary files a/tools/breaklock/assets/icons/icon-152x152.png and /dev/null differ diff --git a/tools/breaklock/assets/icons/icon-192x192.png b/tools/breaklock/assets/icons/icon-192x192.png deleted file mode 100644 index bd52353..0000000 Binary files a/tools/breaklock/assets/icons/icon-192x192.png and /dev/null differ diff --git a/tools/breaklock/assets/icons/icon-256x256.png b/tools/breaklock/assets/icons/icon-256x256.png deleted file mode 100644 index 43f3e1d..0000000 Binary files a/tools/breaklock/assets/icons/icon-256x256.png and /dev/null differ diff --git a/tools/breaklock/assets/icons/ios-180x180.png b/tools/breaklock/assets/icons/ios-180x180.png deleted file mode 100644 index 4273b4c..0000000 Binary files a/tools/breaklock/assets/icons/ios-180x180.png and /dev/null differ diff --git a/tools/breaklock/assets/intro.svg b/tools/breaklock/assets/intro.svg deleted file mode 100644 index ecd63a3..0000000 --- a/tools/breaklock/assets/intro.svg +++ /dev/null @@ -1,541 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/tools/breaklock/assets/ios-startup/startup-1080x1920.png b/tools/breaklock/assets/ios-startup/startup-1080x1920.png deleted file mode 100644 index 426b457..0000000 Binary files a/tools/breaklock/assets/ios-startup/startup-1080x1920.png and /dev/null differ diff --git a/tools/breaklock/assets/ios-startup/startup-640x1136.png b/tools/breaklock/assets/ios-startup/startup-640x1136.png deleted file mode 100644 index 581d903..0000000 Binary files a/tools/breaklock/assets/ios-startup/startup-640x1136.png and /dev/null differ diff --git a/tools/breaklock/assets/ios-startup/startup-640x960.png b/tools/breaklock/assets/ios-startup/startup-640x960.png deleted file mode 100644 index 7d223ae..0000000 Binary files a/tools/breaklock/assets/ios-startup/startup-640x960.png and /dev/null differ diff --git a/tools/breaklock/assets/ios-startup/startup-750x1334.png b/tools/breaklock/assets/ios-startup/startup-750x1334.png deleted file mode 100644 index adf1351..0000000 Binary files a/tools/breaklock/assets/ios-startup/startup-750x1334.png and /dev/null differ diff --git a/tools/breaklock/assets/vectors/cover.svg b/tools/breaklock/assets/vectors/cover.svg deleted file mode 100644 index 16d263b..0000000 --- a/tools/breaklock/assets/vectors/cover.svg +++ /dev/null @@ -1,3250 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - BreakLock_ - - - - - - - - diff --git a/tools/breaklock/assets/vectors/icon.svg b/tools/breaklock/assets/vectors/icon.svg deleted file mode 100644 index e073e02..0000000 --- a/tools/breaklock/assets/vectors/icon.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tools/breaklock/index.html b/tools/breaklock/index.html deleted file mode 100644 index 58aa49b..0000000 --- a/tools/breaklock/index.html +++ /dev/null @@ -1,118 +0,0 @@ - - - - BreakLock - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- $ ./breaklock start
- Loading... - -

- - -
-
- -

Link the dots to find the lock pattern. After every attempt the game will tell you how many dots you got right.

- - - - - - - - - -
a dot occurs in the pattern and is in the correct order
a dot occurs in the pattern but in the wrong order
-

The difficulty setting changes the amount of dots to connect. Easy is 4 dots, medium is 5 dots and hard is 6 dots.

-

Good luck!_

-

by / on

-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tools/breaklock/lab/bruteCalc.js b/tools/breaklock/lab/bruteCalc.js deleted file mode 100644 index 34767ac..0000000 --- a/tools/breaklock/lab/bruteCalc.js +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env node - -if (!process.argv[2] || process.argv[2]) - -var patternLength = parseInt(process.argv[2]), - limit = 9, - proxies = [ - [0,2,1], - [2,0,1], - [2,8,5], - [8,2,5], - [6,8,7], - [8,6,7], - [0,6,3], - [6,0,3], - [0,8,4], - [8,0,4], - [2,6,4], - [6,2,4] - ] - -if (!patternLength || patternLength < 1 || patternLength > limit) { - console.log('Usage: node bruteCalc.js patternLength') - console.log('Please use a valid \'patternLength\' value (between 1 and 9)') - process.exit(1) -} - -function bf (length, stack, buffer) { - buffer = !buffer ? [] : buffer; - if (length <= 0) { - stack.push(buffer) - return stack - } - for (var i = 0; i < limit; i++) { - if (buffer.indexOf(i) != -1) { - continue - } - let pop = buffer[(buffer.length || 1) - 1] - if (buffer.length > 0 && proxies.find(pr => pr[0] == pop && pr[1] == i && buffer.indexOf(pr[2]) == -1)) { - continue - } - - let clone = buffer.concat([]) - clone.push(i) - bf(length-1,stack,clone) - } - return stack -} - -var bfList = bf(patternLength, []) - -bfList.forEach(s => console.log(s.join(''))) -console.log('-----') -console.log('Pattern length : ' + patternLength) -console.log('Lock founds : ' + bfList.length) diff --git a/tools/breaklock/lab/cover_generator.js b/tools/breaklock/lab/cover_generator.js deleted file mode 100644 index 48363f6..0000000 --- a/tools/breaklock/lab/cover_generator.js +++ /dev/null @@ -1,244 +0,0 @@ -/** - * Dirty code to inject into a page to generate - * the cover for the App in SVG - */ - - -/** - * Pattern class - * a pattern is a object representation of a combinaison. - * The amount of dot is specified in the constructor, it's - * not linked to the class itself. - * - * For reference: - * 0 1 2 - * 3 4 5 - * 6 7 8 - * - */ -class Pattern { - constructor (dotLength) { - this.dotLength = dotLength - this.suite = [] - } - - /** - * Fill the current instance with random values - */ - fillRandomly () { - while (!this.isComplete()) { - this.addDot(Math.floor(Math.random() * 9)) - - } - } - - /** - * Add point to the current pattern - * @param {int} dotIndex Dot index to add - * @return boolean True if successfully added - */ - addDot (dotIndex) { - // Test if the dot can be added - if (this.isComplete() || ~this.suite.indexOf(dotIndex)) - return []; - - // Test for potential median dot - let lastDot = this.suite[this.suite.length - 1], - medianDot = (lastDot + dotIndex) / 2 - - if (lastDot != undefined && - medianDot >> 0 === medianDot && - (lastDot%3) - (medianDot%3) === (medianDot%3) - (dotIndex%3) && - Math.floor(lastDot/3) - Math.floor(medianDot/3) === Math.floor(medianDot/3) - Math.floor(dotIndex/3)) { - let addedPoints = this.addDot(medianDot) - if (!this.isComplete()) { - this.suite.push(dotIndex) - addedPoints.push(dotIndex) - } - return addedPoints - } - - this.suite.push(dotIndex) - return [dotIndex] - } - - /** - * Checks if the instance suite is complete - * @return {Boolean} - */ - isComplete () { - return this.suite.length >= this.dotLength - } - - /** - * Checks if a dot is already in the pattern - * @param {int} dotIndex Index to check - * @return {boolean} - */ - gotDot (dotIndex) { - return ~this.suite.indexOf(dotIndex) - } - - compare (pattern) { - var goodPos = 0, - wrongPos = 0 - for (let i = 0; i < this.dotLength; i++) { - if (this.suite[i] === pattern.suite[i]) - goodPos++ - for (let j = 0; j < this.dotLength; j++) { - if (this.suite[j] === pattern.suite[i]) - wrongPos++ - } - } - return [goodPos, wrongPos - goodPos, this.dotLength - wrongPos] - } - - /** - * Reset the pattern by removing all the dots - */ - reset () { - this.suite = [] - } -} - - - - - - - - - - - - - -class PatternSVG { - constructor () { - this.el = document.createElementNS(this.SVG_NAMESPACE, 'svg') - this.el.setAttribute('viewBox', '0 0 ' + (this.GRID_GUTTER * 3 * width) + ' ' + (this.GRID_GUTTER * 3 * height)) - } - - /** - * Add pattern to the instance - * @param {Pattern} pattern Pattern instance to get the points from - * @param {int} size Thickness of the line - * @param {string|array} color List of colors to use for the pattern - * @return PatternSVG - */ - addPattern (pattern, size = 14, color = '#fff', x, y) { - let lineGroup = document.createElementNS(this.SVG_NAMESPACE, 'g') - color = color instanceof Array ? color : [color] - lineGroup.setAttribute('stroke-width', size) - lineGroup.setAttribute('stroke-linecap', 'round') - this.el.appendChild(lineGroup) - - for (let i = 1; i < pattern.suite.length; i++) { - let line = document.createElementNS(this.SVG_NAMESPACE, 'line') - line.setAttribute('x1', (x * 3 * this.GRID_GUTTER) + (pattern.suite[i-1] % 3) * this.GRID_GUTTER) - line.setAttribute('y1', (y * 3 * this.GRID_GUTTER) + Math.floor(pattern.suite[i-1] / 3) * this.GRID_GUTTER) - line.setAttribute('x2', (x * 3 * this.GRID_GUTTER) + (pattern.suite[i] % 3) * this.GRID_GUTTER) - line.setAttribute('y2', (y * 3 * this.GRID_GUTTER) + Math.floor(pattern.suite[i] / 3) * this.GRID_GUTTER) - line.setAttribute('stroke', color[Math.min(color.length, i) - 1]) - lineGroup.appendChild(line) - } - - // Add the dot reprenting the final dot - let lastDotIndex = pattern.suite[pattern.suite.length - 1] - let lastDot = document.createElementNS(this.SVG_NAMESPACE, 'circle') - lastDot.setAttribute('cx', (x * 3 * this.GRID_GUTTER) + (lastDotIndex % 3) * this.GRID_GUTTER) - lastDot.setAttribute('cy', (y * 3 * this.GRID_GUTTER) + Math.floor(lastDotIndex / 3) * this.GRID_GUTTER) - lastDot.setAttribute('fill', color[0]) - lastDot.setAttribute('r', size / 4) - lineGroup.appendChild(lastDot) - - return lineGroup - } - - /** - * Add dots to the instance - * @param {int} size Thickness of the line - * @param {object} attr List of attributes to set to the group - * @return PatternSVG - */ - addDots (size = 3, attr = {}) { - attr.fill = attr.fill || '#fff' - let dotGroup = this.addGroup(attr) - - for (let x = 0; x < 3 * width; x++) { - for (let y = 0; y < 3 * height; y++) { - let circle = document.createElementNS(this.SVG_NAMESPACE, 'circle') - circle.setAttribute('cx', x * this.GRID_GUTTER) - circle.setAttribute('cy', y * this.GRID_GUTTER) - circle.setAttribute('r', size) - dotGroup.appendChild(circle) - } - } - return dotGroup - } - - - /** - * Add the darkbackground - * @return PatternSVG - */ - addBackground () { - let rect = document.createElementNS(this.SVG_NAMESPACE, 'rect') - rect.setAttribute('width', width * 3 * this.GRID_GUTTER) - rect.setAttribute('height', height * 3 * this.GRID_GUTTER) - rect.setAttribute('fill', '#14171b') - this.el.appendChild(rect) - return rect - } - - /** - * Add group to the instance - * @param {object} attr List of attributes to set to the group - * @return PatternSVG - */ - addGroup (attr) { - let group = document.createElementNS(this.SVG_NAMESPACE, 'g') - for (var key in attr) { - group.setAttribute(key, attr[key]) - } - this.el.appendChild(group) - return group - } - - /** - * Get the SVG - * @return SVGDOMElement - */ - getSVG () { - return this.el - } -} - -PatternSVG.prototype.SVG_NAMESPACE = 'http://www.w3.org/2000/svg' -PatternSVG.prototype.SVG_WIDTH = 100 -PatternSVG.prototype.SVG_COMB_EXP = 20 -PatternSVG.prototype.SVG_MARGIN = 15 -PatternSVG.prototype.GRID_GUTTER = 35 -PatternSVG.prototype.DOT_BORDER = 2 -PatternSVG.prototype.DOT_MAGNET = 6 - - - - -var width = 12 -var height = 18 - -var mySVG = new PatternSVG() -mySVG.addBackground() -mySVG.addDots(2) - - -for (let x = 0; x < width; x++) { - for (let y = 0; y < height; y++) { - let pat = new Pattern(4) - pat.fillRandomly() - mySVG.addPattern(pat, 14, ['#999','#ccc','#fff'], x, y) //# TO_DO: Need consts - } -} - -document.body.appendChild(mySVG.el) diff --git a/tools/breaklock/manifest.json b/tools/breaklock/manifest.json deleted file mode 100644 index f726c80..0000000 --- a/tools/breaklock/manifest.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "BreakLock", - "short_name": "BreakLock", - "icons": [{ - "src": "./assets/icons/icon-128x128.png", - "sizes": "128x128", - "type": "image/png" - }, { - "src": "./assets/icons/icon-144x144.png", - "sizes": "144x144", - "type": "image/png" - }, { - "src": "./assets/icons/icon-152x152.png", - "sizes": "152x152", - "type": "image/png" - }, { - "src": "./assets/icons/icon-192x192.png", - "sizes": "192x192", - "type": "image/png" - }, { - "src": "./assets/icons/icon-256x256.png", - "sizes": "256x256", - "type": "image/png" - }], - "start_url": "./", - "display": "standalone", - "orientation": "portrait", - "background_color": "#14171b", - "theme_color": "#14171b" -} diff --git a/tools/breaklock/package.json b/tools/breaklock/package.json deleted file mode 100644 index 41ce7e5..0000000 --- a/tools/breaklock/package.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name": "breaklock", - "version": "0.1.0", - "description": "Small PWA game to guess pattern locks", - "main": "scripts/app.js", - "scripts": { - "build": "./node_modules/webpack/bin/webpack.js --optimize-minimize" - }, - "repository": { - "type": "git", - "url": "https://github.com/maxwellito/breaklock.git" - }, - "author": "maxwellito", - "license": "MIT", - "bugs": { - "url": "https://github.com/maxwellito/breaklock/issues" - }, - "homepage": "https://github.com/maxwellito/breaklock", - "devDependencies": { - "babel-core": "^6.24.0", - "babel-loader": "^6.4.1", - "babel-preset-es2015": "^6.24.0", - "css-loader": "^0.28.0", - "extract-text-webpack-plugin": "^2.1.0", - "node-sass": "^4.5.2", - "raw-loader": "^0.5.1", - "sass-loader": "^6.0.3", - "style-loader": "^0.16.1", - "webpack": "^2.3.3" - } -} diff --git a/tools/breaklock/readme.md b/tools/breaklock/readme.md deleted file mode 100644 index e19b42a..0000000 --- a/tools/breaklock/readme.md +++ /dev/null @@ -1,30 +0,0 @@ -![BreakLock](assets/banner.png) - -### Bring me to the **[game](https://maxwellito.github.io/breaklock/)**! - -Silly HTML5 game, mobile first. -BreakLock is a hybrid of Mastermind and the Android pattern lock. A game you gonna love to hate. - -Hopefully this game (codebase included) will drive you mad. - -The goal of this project was the discover progressive Web apps with service workers, and play with Webpack. Also to entertain because the tube is quite boring, especially the Central line on peak time. - -If you like this game, you must convince 3 people to like it, who will have to convince 3 other people... and this project will turn into the first OSS sect. Also, if you hate it, you must convince 3 people to hate it, who will have to convince 3 other people... - -As long as GitHub provide these sweet gh-pages, this project will be under MIT, without ads, free. - -If you're curious about pattern combinations, run `./lab/bruteCalc.js`. - -## Contribute - -Contributions are welcome, especially pull-requests. There are a lot of ideas to implement, but less people when it come to code. This is why **issues are for bugs only**. - -## Build - -```bash -# Install packages -npm install - -# Build it -npm run build -``` \ No newline at end of file diff --git a/tools/breaklock/service-worker.js b/tools/breaklock/service-worker.js deleted file mode 100644 index 9feefe8..0000000 --- a/tools/breaklock/service-worker.js +++ /dev/null @@ -1,79 +0,0 @@ -var APP_NAME = 'breaklock', - APP_VERSION = 12, - CACHE_NAME = APP_NAME + '_' + APP_VERSION; -var filesToCache = [ - './', - './?utm_source=homescreen', - './app.css', - './app.js', - './assets/intro.svg', - './assets/fonts/robotomono-light-webfont.woff2', - './assets/fonts/robotomono-light-webfont.woff', - './assets/fonts/robotomono-light-webfont.ttf' -]; - -// Service worker from Google Documentation - -self.addEventListener('install', function(event) { - // Perform install steps - event.waitUntil( - caches.open(CACHE_NAME) - .then(function(cache) { - return cache.addAll(filesToCache); - }) - ); -}); - -self.addEventListener('activate', function(event) { - event.waitUntil( - caches.keys().then(function(cacheNames) { - return Promise.all( - cacheNames.map(function(cacheName) { - if (cacheName.indexOf(APP_NAME) === 0 && CACHE_NAME !== cacheName) { - return caches.delete(cacheName); - } - }) - ); - }) - ); -}); - -self.addEventListener('fetch', function(event) { - event.respondWith( - caches.match(event.request) - .then(function(response) { - // Cache hit - return response - if (response) { - return response; - } - - // IMPORTANT: Clone the request. A request is a stream and - // can only be consumed once. Since we are consuming this - // once by cache and once by the browser for fetch, we need - // to clone the response. - var fetchRequest = event.request.clone(); - - return fetch(fetchRequest).then( - function(response) { - // Check if we received a valid response - if(!response || response.status !== 200 || response.type !== 'basic') { - return response; - } - - // IMPORTANT: Clone the response. A response is a stream - // and because we want the browser to consume the response - // as well as the cache consuming the response, we need - // to clone it so we have two streams. - var responseToCache = response.clone(); - - caches.open(CACHE_NAME) - .then(function(cache) { - cache.put(event.request, responseToCache); - }); - - return response; - } - ); - }) - ); -}); diff --git a/tools/breaklock/src/_variables.scss b/tools/breaklock/src/_variables.scss deleted file mode 100644 index 2395915..0000000 --- a/tools/breaklock/src/_variables.scss +++ /dev/null @@ -1,40 +0,0 @@ -$theme_dark: #14171b; -$theme_bright: #ffffff; -$theme_success: #116699; -$theme_error: #ff0000; - - -$gutter: 2rem; -$border: 1px dotted $theme_bright; -$font_weight: 300; - - -// Media queries sizes -$mq-small-min: 0px; -$mq-medium-min: 480px; -$mq-large-min: 768px; -$mq-xlarge-min: 1024px; -$mq-xxlarge-min: 1600px; - -$mq-small-max: $mq-medium-min - 1px; -$mq-medium-max: $mq-large-min - 1px; -$mq-large-max: $mq-xlarge-min - 1px; -$mq-xlarge-max: $mq-xxlarge-min - 1px; -$mq-xxlarge-max: 5120px; - - -// Mixins -%button-feel { - @extend %unselectable; - cursor: pointer; -} - -%unselectable { - -webkit-user-select: none; /* Chrome/Safari */ - -moz-user-select: none; /* Firefox */ - -ms-user-select: none; /* IE10+ */ - - /* Rules below not implemented in browsers yet */ - -o-user-select: none; - user-select: none; -} diff --git a/tools/breaklock/src/app.js b/tools/breaklock/src/app.js deleted file mode 100644 index fdb6781..0000000 --- a/tools/breaklock/src/app.js +++ /dev/null @@ -1,31 +0,0 @@ -import GameCtrl from './controllers/game/game.ctrl' -import MenuCtrl from './controllers/menu/menu.ctrl' - -require('./style.scss'); - -var introDom = document.getElementById('app-intro') -if (introDom) { - introDom.remove() -} - -var container = document.body - -var myGame = new GameCtrl(() => { - myMenu.el.style.display = '' - myGame.el.style.display = 'none' -}) -container.appendChild(myGame.el) - -var myMenu = new MenuCtrl((type, difficulty) => { - myGame.start(type, difficulty) - - myMenu.el.style.display = 'none' - myGame.el.style.display = '' -}) -myMenu.init() -container.appendChild(myMenu.el) - -myGame.el.style.display = 'none' - - -window.scrollTo(0, 0) diff --git a/tools/breaklock/src/config.js b/tools/breaklock/src/config.js deleted file mode 100644 index 736d6db..0000000 --- a/tools/breaklock/src/config.js +++ /dev/null @@ -1,54 +0,0 @@ -const config = { - GAME: { - DIFFICULTY: { - EASY: 4, - MEDIUM: 5, - HARD: 6 - }, - TYPE: { - PRACTICE: 1, - CHALLENGE: 2, - COUNTDOWN: 3 - }, - ACTIONS: { - CONTINUE: 1, - NEW_GAME: 2, - BACK_HOME: 3 - } - }, - SOCIAL: { - PLATFORMS: { - FB: { - NAME: 'Facebook', - ICON: 'facebook', - URL: (url) => `https://www.facebook.com/sharer/sharer.php?u=${encodeURI(url)}` - }, - TWITTER: { - NAME: 'Twitter', - ICON: 'twitter', - URL: (url, msg, tags) => { - return `http://twitter.com/` + - (url ? `share?` : `intent/tweet?`) + - (msg ? `text=${encodeURI(msg)}&` : '') + - (url ? `url=${encodeURI(url)}&` : '') + - (tags ? `hashtags=${encodeURI(tags.join(','))}` : '') - } - } - }, - MESSAGE: 'I wasted my time on BreakLock, it\'s pointless, don\'t try it.', - TAGS: ['breaklock'] - }, - URL: 'https://maxwellito.github.io/breaklock/', - COLORS: { - BRIGHT: '#ffffff', - DARK: '#14171b', - SUCCESS: '#116699', - ERROR: '#ff0000' - }, - PATTERN: { - HEX_COLOR_START: '66', - HEX_COLOR_END: 'FF' - } -} - -export default config diff --git a/tools/breaklock/src/controllers/countdown/countdown.ctrl.js b/tools/breaklock/src/controllers/countdown/countdown.ctrl.js deleted file mode 100644 index 396c8e8..0000000 --- a/tools/breaklock/src/controllers/countdown/countdown.ctrl.js +++ /dev/null @@ -1,98 +0,0 @@ -import leftPadNum from '../../utils/leftPadNum' -import dom from '../../utils/dom' - -require('./countdown.scss'); - -/** - * Countdown Controller - * Basic component representing a small countdown - * bar to be used in the status bar. - */ -class CountdownCtrl { - - /** - * Not much here... just buildin the template - */ - constructor () { - this.setupTemplate() - } - - /** - * Build template of the controller - * @return {DOMElement} - */ - setupTemplate () { - this.counterEl = dom.create('span', 'countdown-counter') - this.barEl = dom.create('span', 'countdown-content') - - let container = dom.create('span', 'countdown-container', [ - this.barEl - ]) - - this.el = dom.create('div', 'countdown', [ - this.counterEl, - container - ]) - return this.el - } - - /** - * Set the countdown. - * @param {int} duration Duration in seconds - * @param {function} callback Callback to call on end - */ - setTimer (duration, callback) { - this.duration = duration - this.remaining = duration - this.endCallback = callback - this.render() - } - - /** - * Starts the countdown - * - */ - start () { - if (this.interval) - return - this.interval = window.setInterval(this.decrement.bind(this), 1000) - } - - /** - * Stops the countdown - * - */ - stop () { - window.clearInterval(this.interval) - this.interval = null - } - - /** - * Decrement the counter by one second - * - */ - decrement () { - this.remaining-- - this.render() - } - - /** - * Render the component by using the values of - * the instance. If the countdown is negative or - * null, the end callback will be triggered - */ - render () { - this.remaining = this.remaining > 0 ? this.remaining : 0 - - this.el.classList[this.remaining > 10 ? 'remove' : 'add']('alert') - this.counterEl.textContent = leftPadNum(this.remaining, 3) - this.barEl.style.width = (this.remaining / this.duration * 100) + '%' - - if (this.remaining == 0) { - this.stop() - this.endCallback && this.endCallback() - } - } -} - -export default CountdownCtrl diff --git a/tools/breaklock/src/controllers/countdown/countdown.scss b/tools/breaklock/src/controllers/countdown/countdown.scss deleted file mode 100644 index cd27f2d..0000000 --- a/tools/breaklock/src/controllers/countdown/countdown.scss +++ /dev/null @@ -1,26 +0,0 @@ -@import "../../_variables.scss"; - -.countdown-container { - float: right; - width: 10rem; - height: 1rem; - margin: .25rem 0 .25rem .5rem; - border: 1px solid currentColor; - border-radius: .25rem; - box-sizing: border-box; - color: inherit; -} - -.alert { - color: red; - animation: blink 1s infinite; - -webkit-animation: blink 1s infinite; -} - -.countdown-content { - display: block; - height: 100%; - width: 0; - color: inherit; - background-color: currentColor; -} \ No newline at end of file diff --git a/tools/breaklock/src/controllers/extender/extender.ctrl.js b/tools/breaklock/src/controllers/extender/extender.ctrl.js deleted file mode 100644 index 98e02f2..0000000 --- a/tools/breaklock/src/controllers/extender/extender.ctrl.js +++ /dev/null @@ -1,72 +0,0 @@ -import dom from '../../utils/dom' - -require('./extender.scss'); - -/** - * Extender class - * Simple controller to show/hide long content - * with a button. - */ -class ExtenderCtrl { - - /** - * Every instance require a title which is the dropdown - * button text, the content that can be a string or - * a DOM element to display, and the initial state - * of the extender. - * @param {String} title Button title - * @param {String} content Content text of DOM element to display - * @param {Boolean} isExpanded Initial state of the controller - */ - constructor (title, content, isExpanded) { - this.title = title - this.content = content - this.isExpanded = isExpanded - - this.setupTemplate() - } - - /** - * Build template of the controller - * @return {DOMElement} - */ - setupTemplate () { - let content = this.content instanceof String ? this.content : [this.content] - this.buttonEl = dom.create('button', 'extender-button', this.title) - this.contentEl = dom.create('div', 'extender-content', content) - - this.el = dom.create('div', 'extender small-only', [ - this.buttonEl, - this.contentEl - ]) - - this.render() - return this.el - } - - /** - * Set up listeners - */ - init () { - this.buttonEl.addEventListener('click', this.toggle.bind(this)) - } - - /** - * Show/hide the content - * @param {Boolean} force Force to show or hide if provided - */ - toggle (force) { - this.isExpanded = (force instanceof Boolean) ? force : !this.isExpanded - this.render() - } - - /** - * Render the DOM from the state of the controller - * - */ - render () { - this.el.classList[this.isExpanded ? 'add' : 'remove']('active') - } -} - -export default ExtenderCtrl diff --git a/tools/breaklock/src/controllers/extender/extender.scss b/tools/breaklock/src/controllers/extender/extender.scss deleted file mode 100644 index 8b28bc6..0000000 --- a/tools/breaklock/src/controllers/extender/extender.scss +++ /dev/null @@ -1,45 +0,0 @@ -@import "../../_variables.scss"; - -// Not much for now... -.extender { - -} - -.extender-button { - display: block; - width: 100%; - padding: .25em; - text-align: center; - background: rgba(255,255,255,.125); - border-bottom: $border; - - &:after { - // ▲▼▾▴△▽ - content: '▽'; - margin-left: .5em; - } - - .extender.active &:after { - // ▲▼▾▴△▽ - content: '△'; - } -} - -.extender-content { - display: none; - - .extender.active & { - display: inherit; - } -} - -@media (orientation: landscape), (min-width: $mq-xlarge-min) { - .extender.small-only { - .extender-button { - display: none; - } - .extender-content { - display: inherit; - } - } -} diff --git a/tools/breaklock/src/controllers/game/game.ctrl.js b/tools/breaklock/src/controllers/game/game.ctrl.js deleted file mode 100644 index b4f3fec..0000000 --- a/tools/breaklock/src/controllers/game/game.ctrl.js +++ /dev/null @@ -1,210 +0,0 @@ -import StatusBarCtrl from '../statusBar/statusBar.ctrl' -import HistoryCtrl from '../history/history.ctrl' -import LockCtrl from '../lock/lock.ctrl' -import SummaryCtrl from '../summary/summary.ctrl' -import Pattern from '../../models/pattern' -import PatternSVG from '../../utils/patternSVG' -import config from '../../config' -import dom from '../../utils/dom' -import color from '../../utils/color' - -require('./game.scss'); - -/** - * Game Controller - * The playground, the arena - * It combines a status bar, a pattern history - * and a lock. - */ -class GameCtrl { - - /** - * Setup the controller - * The callback provided will be called with - * different parameter depending on the type - * of end (abort, success, fail) - * @param {function} onEnd Callback for end of game - * @return {[type]} [description] - */ - constructor (onEnd) { - // Lets leave it empty for now - // just init the shite to help V8 - this.statusBar = new StatusBarCtrl(this.abort.bind(this)) - this.history = new HistoryCtrl() - this.lock = new LockCtrl(this.newAttempt.bind(this)) - this.summary = new SummaryCtrl(this.action.bind(this)) - this.pattern = null - this.type = null - this.isEnded = false - this.onEnd = onEnd - - this.statusBar.init() - this.lock.init() - - this.setupTemplate() - } - - /** - * Build template of the controller - * @return {SVGDOMElement} - */ - setupTemplate () { - this.el = dom.create('div', 'game-layout view', [ - dom.create('div', 'view-bloc game-layout-dashboard', [ - this.statusBar.el, - dom.create('div', 'history-wrap', [this.history.el]) - ]), - dom.create('div', 'view-bloc game-layout-lock', [this.lock.el]), - this.summary.el - ]) - return this.el - } - - - /* Controls **********************************/ - - /** - * Start a new game - * @param {int} type Type ID - * @param {int} difficulty Number of dots - */ - start (type, difficulty) { - this.type = type - this.difficulty = difficulty - this.lock.setDotLength(difficulty) - this.pattern = new Pattern(difficulty) - this.pattern.fillRandomly() - this.history.clear('Connect ' + difficulty + ' dots') - this.count = 0 - this.isEnded = false - - switch (type) { - case config.GAME.TYPE.PRACTICE: - return this.statusBar.setCounter(0) - case config.GAME.TYPE.CHALLENGE: - return this.statusBar.setCounter(10) - case config.GAME.TYPE.COUNTDOWN: - return this.statusBar.setCountdown(60) - } - } - - /** - * Listener for new pattern drawn by the user - * and provided via the the Lock controller. - * @param {Pattern} pattern Pattern to test - * @return {Boolean} True if the pattern is correct - */ - newAttempt (pattern) { - // Generate a SVG from the pattern provided - let match = this.pattern.compare(pattern), - svgPattern = this.buildPatternSVG(pattern, match), - isUnlocked = (match[0] === this.pattern.dotLength) - - this.count++ - - if (this.isEnded) { - this.statusBar.incrementCounter() - } - else if (isUnlocked) { - // Success case - if (this.type === config.GAME.TYPE.COUNTDOWN) - this.statusBar.stopCountdown() - - this.isEnded = svgPattern - this.summary.setContent(true, this.count) - } - else { - // Fail case - switch (this.type) { - case config.GAME.TYPE.PRACTICE: - this.statusBar.incrementCounter() - break - case config.GAME.TYPE.CHALLENGE: - if (this.statusBar.decrementCounter() === 0) { - this.isEnded = true - this.summary.setContent(false, this.count) - } - break - } - } - this.history.stackPattern(svgPattern) - return isUnlocked - } - - /** - * Cancel listener for the status bar - * @param {Number} exitCode Exit code from the status bar - */ - abort (exitCode) { - if (exitCode) { - // Exit from countdown - this.isEnded = true - this.statusBar.stopCountdown() - this.summary.setContent(false, this.count) - } - else { - // Abort from the user - this.onEnd() - } - } - - /** - * Action listener for the action of the summary - * controller. To continue, try again or go back - * to the home menu. - * @param {Number} actionId Action code to apply - */ - action (actionId) { - switch (actionId) { - case config.GAME.ACTIONS.NEW_GAME: - this.start(this.type, this.difficulty) - break; - case config.GAME.ACTIONS.BACK_HOME: - this.abort() - break; - case config.GAME.ACTIONS.CONTINUE: - // Nothing for now - debugger - if (this.isEnded === true) { - let match = this.pattern.compare(this.pattern) - let svgPattern = this.buildPatternSVG(this.pattern, match) - this.history.stackPattern(svgPattern) - } - this.statusBar.setCounter(this.count) - break; - } - this.summary.toggle() - } - - /** - * Generate the pattern SVG from a pattern object - * - * @param {Pattern} pattern Pattern object to use to generate the SVG - * @param {Array} match Pattern match array - * @return {SVGDOMElement} - */ - buildPatternSVG (pattern, match) { - // Generate a SVG from the pattern provided - let attemptSVG = new PatternSVG() - attemptSVG.addDots(1) - attemptSVG.addPattern(pattern, 14, color.greydient( - config.PATTERN.HEX_COLOR_START, - config.PATTERN.HEX_COLOR_END, - pattern.dotLength - 3 - )) - - // Add the feedback on the SVG - if (match) - PatternSVG.prototype.addCombinaison.apply(attemptSVG, match) - - let svgPattern = attemptSVG.getSVG() - - // Add the success class to the SVG - if ((match[0] === pattern.dotLength)) - svgPattern.classList.add('success') - - return svgPattern - } -} - -export default GameCtrl diff --git a/tools/breaklock/src/controllers/game/game.scss b/tools/breaklock/src/controllers/game/game.scss deleted file mode 100644 index fe1d23e..0000000 --- a/tools/breaklock/src/controllers/game/game.scss +++ /dev/null @@ -1,20 +0,0 @@ -@import "../../_variables.scss"; - -@media (max-width: $mq-large-max) { - .history-wrap { - border-bottom: $border; - - .history { - margin: 0 #{-$gutter}; - } - .history-container { - padding: 0 #{$gutter}; - } - } -} - -@media (orientation: landscape), (min-width: $mq-xlarge-min) { - .game-layout-lock { - align-self: center; - } -} diff --git a/tools/breaklock/src/controllers/history/history.ctrl.js b/tools/breaklock/src/controllers/history/history.ctrl.js deleted file mode 100644 index 51ffeda..0000000 --- a/tools/breaklock/src/controllers/history/history.ctrl.js +++ /dev/null @@ -1,79 +0,0 @@ -import dom from '../../utils/dom' - -require('./history.scss'); - -/** - * History Controller - * Simple controller stacking the previous pattern - * tested by the user. This is a simple dummy container - * stacking all the SVGs - * The class used is .history, for optimisation all - * SVGs will use `will-change` or `transformZ` properties - * to improve performance. - */ -class HistoryCtrl { - - /** - * Set up instance - */ - constructor () { - this.lastPattern = null - this.setupTemplate() - } - - /** - * Build template of the controller - * @return {SVGDOMElement} - */ - setupTemplate () { - this.containerEl = dom.create('div', 'history-container', '') - this.el = dom.create('div', 'history', [this.containerEl]) - return this.el - } - - /** - * Add a new pattern in the container - * @param {SVGDOM} pattern The try to stack - */ - stackPattern (pattern) { - if (this.lastPattern) - this.containerEl.insertBefore(pattern, this.lastPattern) - else - this.containerEl.appendChild(pattern) - this.lastPattern = pattern - this.scrollToStart() - } - - /** - * Loop animation to scroll smoothly - * the history to the start. - */ - scrollToStart () { - let pos = this.el.scrollLeft - this.el.scrollLeft = (pos - Math.max(pos / 4, 4), 0) - - if (this.el.scrollLeft > 0) { - window.requestAnimationFrame(this.scrollToStart.bind(this)) - } - } - - /** - * Clean the history - * - * @param {String} helperText Helper text displayed when the history is empty - * @return {[type]} - */ - clear (helperText) { - this.lastPattern = null - - // Clean the container - this.containerEl.remove() - this.containerEl = dom.create('div', { - class: 'history-container', - 'data-helper': helperText - }) - this.el.appendChild(this.containerEl) - } -} - -export default HistoryCtrl diff --git a/tools/breaklock/src/controllers/history/history.scss b/tools/breaklock/src/controllers/history/history.scss deleted file mode 100644 index c6492ba..0000000 --- a/tools/breaklock/src/controllers/history/history.scss +++ /dev/null @@ -1,100 +0,0 @@ -@import "../../_variables.scss"; - -$patternHeight: 5rem; -$patternMargin: 1rem; -$patternRatio: 100/120; -// I really wanted to use width: auto. -// But not all browsers are ok with it - -.history { - height: $patternHeight + (2 * $patternMargin); - overflow-x: scroll; - font-size: 0; - line-height: 0; - white-space: nowrap; - -webkit-backface-visibility: hidden; - backface-visibility: hidden; -} - -.history-container { - display: table-cell; - vertical-align: top; - - &:empty { - display: block; - &:after { - content: attr(data-helper); - display: block; - line-height: 6; - font-size: 1rem; - text-align: center; - } - } -} - -.history-start-helper { - display: none; -} - -.history svg { - width: $patternHeight * $patternRatio; - height: $patternHeight; - display: inline-block; - margin: $patternMargin 0; - transform: translateZ(0); - animation: expand .25s, fadein .5s linear; - -webkit-transform: translateZ(0); - -webkit-animation: expand .25s, fadein .5s linear; - - &.success { - border: #{$patternMargin/8} solid currentColor; - border-radius: #{$patternMargin/4}; - padding: #{$patternMargin/8*3} #{$patternMargin/4}; - margin-top: #{$patternMargin/2}; - margin-bottom: #{$patternMargin/2}; - } - - & + svg { - margin-left: $patternMargin; - } -} - -@keyframes fadein { - 0% {opacity: 0;} - 50% {opacity: 0;} - 100% {opacity: 1;} -} - -@-webkit-keyframes fadein { - 0% {opacity: 0;} - 50% {opacity: 0;} - 100% {opacity: 1;} -} - -@keyframes expand { - 0% {width: 0; padding: 0;} -} - -@-webkit-keyframes expand { - 0% {width: 0; padding: 0;} -} - -@media (orientation: landscape), (min-width: $mq-xlarge-min) { - .history { - height: auto; - border-bottom: none; - } - - .history-container { - display: block; - } - - .history svg { - float: left; - margin-right: $patternMargin; - - & + svg { - margin-left: 0; - } - } -} diff --git a/tools/breaklock/src/controllers/lock/lock.ctrl.js b/tools/breaklock/src/controllers/lock/lock.ctrl.js deleted file mode 100644 index ea89f22..0000000 --- a/tools/breaklock/src/controllers/lock/lock.ctrl.js +++ /dev/null @@ -1,311 +0,0 @@ -import Pattern from '../../models/pattern' -import PatternSVG from '../../utils/patternSVG' -import dom from '../../utils/dom' -import config from '../../config' - -require('./lock.scss'); - -/** - * Lock class - * Component of the lock itself - * It will listen for user input, update the view - * and trigger the other instances when required. - */ -class LockCtrl { - - /** - * Make it ready - * @param {Function} callback Callback to call on new pattern - */ - constructor (callback) { - this.currentLine = null - this.onNewPattern = callback - - this.setupTemplate() - } - - /** - * Build template of the controller - * @return {SVGDOMElement} - */ - setupTemplate () { - let myPatternSVG = new PatternSVG() - myPatternSVG.addBackgroundLayer() - - this.el = myPatternSVG.getSVG() - this.el.setAttribute('class', 'lock') - this.patternEl = myPatternSVG.addGroup({ - 'stroke-width': '2', - 'stroke': config.COLORS.BRIGHT, - 'stroke-linecap': 'round' - }) - this.bigDotsEl = myPatternSVG.addDots(9, {class: 'lock-flashdots'}) - - myPatternSVG.addDots(2) - return this.el - } - - /** - * Start listening to user input - */ - init () { - // start listening for events (fingers) - this.el.addEventListener('touchstart', this.touchStart.bind(this)) - this.el.addEventListener('touchmove', this.touchUpdate.bind(this)) - this.el.addEventListener('touchend', this.touchEnd.bind(this)) - - // start listening for mouse - this.el.addEventListener('mousedown', this.mouseStart.bind(this)) - } - - /** - * Set the pattern length - * @param {int} dotLength Number of dots in the pattern - */ - setDotLength (dotLength) { - this.dotLength = dotLength - this.pattern = new Pattern(this.dotLength) - } - - /** - * Listeners - */ - - /* Mouse listeners *************************************/ - - /** - * Listener for mouse down on the lock. - * It will start listening to mouse move and stop. - * @param {MouseEvent} t Mouse down event - */ - mouseStart (t) { - this.reset() - - this.mouseUpdateBind = this.mouseUpdate.bind(this) - this.mouseEndBind = this.mouseEnd.bind(this) - this.el.addEventListener('mousemove', this.mouseUpdateBind) - window.addEventListener('mouseleave', this.mouseEndBind) - window.addEventListener('mouseup', this.mouseEndBind) - this.mouseUpdate(t) - } - - /** - * Listener for mouse move while drowing a pattern - * with the mouse. - * @param {MouseEvent} t Mouse move event - */ - mouseUpdate (t) { - t.preventDefault() - t.stopPropagation() - - let e = t.currentTarget.getBoundingClientRect(), - x = Math.max(0, Math.min(PatternSVG.prototype.SVG_WIDTH, Math.round(PatternSVG.prototype.SVG_WIDTH / e.width * (t.pageX - e.left)))), - y = Math.max(0, Math.min(PatternSVG.prototype.SVG_WIDTH, Math.round(PatternSVG.prototype.SVG_WIDTH / e.height * (t.pageY - e.top )))) - - this.updatePoint(x, y) - } - - /** - * Method to end drawing with mouse. - * It will remove useless listeners and reset - * the current pattern. - * @param {MouseEvent} t Mouse event - */ - mouseEnd (t) { - if (!this.isPendingReset) - this.reset() - this.el.removeEventListener('mousemove', this.mouseUpdateBind) - window.removeEventListener('mouseout', this.mouseEndBind) - window.removeEventListener('mouseup', this.mouseEndBind) - } - - /* Touch listeners *************************************/ - - /** - * Listener for startring drwaing a pattern - * with touch events. - * This will only reset the current pattern - * and start drawing the new one. - * @param {Event} t Touch Start event - */ - touchStart (t) { - this.reset() - this.touchUpdate(t) - } - - /** - * Listener for touch events - * The method will calculate the position of the finger - * on the lock to update the line and add dots to the - * current pattern. - * @param {Event} t Touch event - */ - touchUpdate (t) { - t.preventDefault() - t.stopPropagation() - - let e = t.currentTarget.getBoundingClientRect(), - x = Math.max(0, Math.min(PatternSVG.prototype.SVG_WIDTH, Math.round(PatternSVG.prototype.SVG_WIDTH / e.width * (t.targetTouches[0].pageX - e.left)))), - y = Math.max(0, Math.min(PatternSVG.prototype.SVG_WIDTH, Math.round(PatternSVG.prototype.SVG_WIDTH / e.height * (t.targetTouches[0].pageY - e.top)))) - this.updatePoint(x, y) - } - - /** - * Listener for end of pattern drawing - * with touch events. - */ - touchEnd () { - if (!this.isPendingReset) - this.reset() - } - - /* - * Drawing logic - */ - - /** - * Update the current pattern by providing - * the position of the new cursor/finger - * in ordinates scaled to SVG size. - * @param {Number} x Position X of pointer/finger - * @param {Number} y Position X of pointer/finger - */ - updatePoint (x, y) { - // Cancel if the current pattern is finished - if (this.isPendingReset) - return - - let iX, iY - for (let i = 0; i < 3; i++) { - let rangeStart = PatternSVG.prototype.GRID_GUTTER * i + PatternSVG.prototype.SVG_MARGIN - PatternSVG.prototype.DOT_MAGNET, - rangeEnd = PatternSVG.prototype.GRID_GUTTER * i + PatternSVG.prototype.SVG_MARGIN + PatternSVG.prototype.DOT_MAGNET - iX = (rangeStart <= x && rangeEnd >= x) ? i : iX - iY = (rangeStart <= y && rangeEnd >= y) ? i : iY - } - - let isEndOfPattern - if (iX !== undefined && iY != undefined) { - let dotIndex = iY * 3 + iX - isEndOfPattern = this.triggerDot(dotIndex) - } - if (!isEndOfPattern) - this.updateLine(x, y) - - return true - } - - /** - * Add a dot on the current pattern and the - * intermediate ones if there's any. - * @param {number} dotIndex Dot triggered index - */ - triggerDot (dotIndex) { - // Check if the current pattern got the dot - if (this.pattern.gotDot(dotIndex)) - return - - // Get the list new dots from the one triggered. - // This method return a list because adding a - // dot might add some intermediate others. - var newDots = this.pattern.addDot(dotIndex) - - if (navigator.vibrate) - navigator.vibrate(20) - - newDots.forEach((dot, index) => { - let dotX = PatternSVG.prototype.GRID_GUTTER * (dot % 3) + PatternSVG.prototype.SVG_MARGIN, - dotY = PatternSVG.prototype.GRID_GUTTER * Math.floor(dot / 3) + PatternSVG.prototype.SVG_MARGIN - - // Close current line - this.closeLine(dotX, dotY) - this.bigDotsEl.childNodes[dot].classList.add('active') - - // Check if finished - if ((index + 1) === newDots.length && this.pattern.isComplete()) - // The drawing here - return this.checkPattern() - else - // Start new one - this.startLine(dotX, dotY) - }) - } - - /** - * Reset the lock - */ - reset () { - // Clear timeout - clearTimeout(this.isPendingReset) - this.isPendingReset = null - - // Reset pattern - this.pattern.reset() - - // Clear existing pattern - this.currentLine = null - for (let i = 0; i < 9; i++) - this.bigDotsEl.childNodes[i].classList.remove('active') - for (let i = this.patternEl.childNodes.length - 1; i >= 0; i--) - this.patternEl.childNodes[i].remove() - this.patternEl.setAttribute('stroke', config.COLORS.BRIGHT) - } - - /** - * Procedure for new patterns - * @return {Boolean} True if the pattern tested is correct - */ - checkPattern () { - let itsAmatch = this.onNewPattern(this.pattern) - - this.isPendingReset = setTimeout(this.reset.bind(this), 1000) - - // for (let i = this.patternEl.childNodes.length - 1; i >= 0; i--) - this.patternEl.setAttribute('stroke', itsAmatch ? config.COLORS.SUCCESS : config.COLORS.ERROR) - - return itsAmatch - } - - /* - * Drawn pattern - */ - - /** - * Start a new 'current line'. - * The current line is the one in progress of being - * manipulated. - * @param {int} x Position X of the line starting point - * @param {int} y Position Y of the line starting point - */ - startLine (x, y) { - this.currentLine = dom.create('line', { - x1: x, - y1: y - }) - this.patternEl.appendChild(this.currentLine) - } - - /** - * Update the line of the current move - * @param {int} x Position X on the finger on the SVG scale - * @param {int} y Position Y on the finger on the SVG scale - */ - updateLine (x, y) { - if (!this.currentLine) - return - - this.currentLine.setAttribute('x2', x) - this.currentLine.setAttribute('y2', y) - } - - /** - * Update and close the line of the current move - * @param {int} x Position X of the line ending point - * @param {int} y Position Y of the line ending point - */ - closeLine (x, y) { - this.updateLine(x, y) - this.currentLine = null - } -} - -export default LockCtrl diff --git a/tools/breaklock/src/controllers/lock/lock.scss b/tools/breaklock/src/controllers/lock/lock.scss deleted file mode 100644 index 2e9b791..0000000 --- a/tools/breaklock/src/controllers/lock/lock.scss +++ /dev/null @@ -1,19 +0,0 @@ -@import "../../_variables.scss"; - -.lock { - display: block; - width: 320px; - max-width: 90%; - margin: auto; - - &-flashdots circle { - visibility: hidden; - opacity: 1; - - &.active { - visibility: visible; - opacity: 0; - transition: visibility 0s, opacity .3s; - } - } -} diff --git a/tools/breaklock/src/controllers/menu/menu.ctrl.js b/tools/breaklock/src/controllers/menu/menu.ctrl.js deleted file mode 100644 index bd6f1db..0000000 --- a/tools/breaklock/src/controllers/menu/menu.ctrl.js +++ /dev/null @@ -1,126 +0,0 @@ -import ExtenderCtrl from '../extender/extender.ctrl' -import OptionCtrl from '../option/option.ctrl' -import SelectorCtrl from '../selector/selector.ctrl' -import config from '../../config' -import dom from '../../utils/dom' -import airportText from '../../utils/airportText' - -require('./menu.scss'); - -/** - * Menu Controller - * Where all the magic starts! - * The welcoming screen with all the different settings - * to know what game to sart. - */ -class MenuCtrl { - - /** - * ¯\_(ツ)_/¯ - */ - constructor (onStart) { - this.onStart = onStart - this.setupTemplate() - } - - /** - * Build template of the controller - * @return {DOMElement} - */ - setupTemplate () { - let title = dom.create('h1', 'menu-title highlight unselectable', 'BreakLock'), - intro = dom.create('p', 'menu-intro', 'A hybrid of Mastermind and the Android pattern lock. A game you gonna love to hate.') - this.title = title; - this.typeHelpEl = dom.create('p', {}, 'Future info about the challenge') - this.btnStarlEl = dom.create('button', 'action-btn', 'START_') - - airportText(title, 'BreakLock') - - let instructions = new ExtenderCtrl('INSTRUCTIONS', document.getElementById('instructions-template')) - instructions.init() - - // Options - this.difficultyOption = new OptionCtrl([ - { value: config.GAME.DIFFICULTY.EASY, label: 'Easy', default: true}, - { value: config.GAME.DIFFICULTY.MEDIUM, label: 'Medium'}, - { value: config.GAME.DIFFICULTY.HARD, label: 'Hard'} - ]) - - this.typeSelector = new SelectorCtrl([ - { - value: config.GAME.TYPE.PRACTICE, - label: 'Practice', - description: 'No pressure, just discover and practice your game', - default: true - }, - { - value: config.GAME.TYPE.CHALLENGE, - label: 'Challenge', - description: 'Challenge mode give you 10 attempts only to win' - }, - { - value: config.GAME.TYPE.COUNTDOWN, - label: 'Countdown', - description: 'Solve the game in one minute, without limit of attempts' - } - ]) - - this.el = dom.create('div', 'menu-layout view', [ - dom.create('div', 'view-bloc menu-layout-instructions', [ - title, - intro, - instructions.el - ]), - dom.create('div', 'view-bloc menu-layout-form', [ - this.difficultyOption.el, - this.typeSelector.el, - this.typeHelpEl, - this.btnStarlEl - ]) - ]) - return this.el - } - - /** - * Set up listeners - */ - init () { - this.typeSelector.init() - this.typeSelector.onSelect(this.typeChange.bind(this)) - this.btnStarlEl.addEventListener('click', this.start.bind(this)) - this.title.addEventListener('dblclick', this.triggerEasterEgg.bind(this)) - } - - /** - * Start a new game by calling the callback - * provided in the controller. - */ - start () { - this.onStart(this.typeSelector.getValue(), this.difficultyOption.getValue()) - } - - /** - * Selector for new type - * @param {object} type New selected type - */ - typeChange (type) { - this.typeHelpEl.textContent = type.description - } - - /** - * Double click listener to trigger the OLED - * screen mode for a deep black design. - */ - triggerEasterEgg () { - if (localStorage.getItem('isDeepBlack')) { - localStorage.setItem('isDeepBlack', '') - document.body.classList.remove('deepblack') - } - else { - localStorage.setItem('isDeepBlack', 'on') - document.body.classList.add('deepblack') - } - } -} - -export default MenuCtrl diff --git a/tools/breaklock/src/controllers/menu/menu.scss b/tools/breaklock/src/controllers/menu/menu.scss deleted file mode 100644 index c1f1446..0000000 --- a/tools/breaklock/src/controllers/menu/menu.scss +++ /dev/null @@ -1,26 +0,0 @@ -@import "../../_variables.scss"; - -.menu-title { - margin: 0; -} - -.menu-layout-instructions { - -webkit-flex:1 0 auto; -} - -.menu-layout-form { - max-width: 480px; -} - -.introduction-demo { - display: block; - width: 100%; - max-width: 320px; - margin: 1rem auto; -} - -@media (orientation: landscape), (min-width: $mq-xlarge-min) { - .menu-layout { - align-items: center; - } -} diff --git a/tools/breaklock/src/controllers/option/option.ctrl.js b/tools/breaklock/src/controllers/option/option.ctrl.js deleted file mode 100644 index 5696c11..0000000 --- a/tools/breaklock/src/controllers/option/option.ctrl.js +++ /dev/null @@ -1,100 +0,0 @@ -import dom from '../../utils/dom' - -require('./option.scss'); - -/** - * Option Controller - * Component to build a one line selector - * between different options - * The class used for the parent element - * is .option, all childs are .option-item - */ -class OptionCtrl { - - /** - * Setup the template and different options - * See `setChoices` method to understand the - * format of the following parameters - * @param {Array} choiceList List of key/values to display - */ - constructor (choiceList, defaultChoice) { - this.setupTemplate() - this.setChoices(choiceList) - } - - /** - * Build template of the controller - * @return {DOMElement} - */ - setupTemplate () { - this.el = dom.create('div', 'selectbox') - return this.el - } - - /** - * Set up the different available choices - * Choice list format: - * [ - * { value: , label: , default: }, - * { value: 2, label: 'Easy'}, - * { value: 3, label: 'Medium', default: true}, - * { value: 4, label: 'Hard'}, - * ] - * @param {Array} choiceList List of options to display - */ - setChoices (choiceList) { - let listener = this.selectListener.bind(this) - choiceList.forEach((choice, index) => { - let option = dom.create('span', { - class: 'selectbox-item', - rel: choice.value - }, choice.label) - option.addEventListener('click', listener) - option.addEventListener('touchstart', listener) - this.el.appendChild(option) - - if (choice.default) - this.selectFromTag(option) - - return option - }) - this.el.classList.add('selectbox-' + choiceList.length) - } - - /** - * Listener for click on items - * @param {Event} event Event catched - */ - selectListener (e) { - e.preventDefault() - e.stopPropagation() - this.selectFromTag(e.currentTarget) - } - - /** - * Update the selected value from the tag (:item) - * provided in parameter. The call will apply the - * class selected to the new tag (and remove it to - * the previous one), then also update the selected - * value of the instance. - * @param {DOMElement} tag Element tag selected - */ - selectFromTag (tag) { - if (this.selectedTag) - this.selectedTag.classList.remove('active') - - this.selectedTag = tag - this.selectedTag.classList.add('active') - this.selectedValue = window.parseInt(tag.getAttribute('rel'), 10) - } - - /** - * Return the current choice selected - * @return {int} - */ - getValue () { - return this.selectedValue - } -} - -export default OptionCtrl diff --git a/tools/breaklock/src/controllers/option/option.scss b/tools/breaklock/src/controllers/option/option.scss deleted file mode 100644 index ceea2f4..0000000 --- a/tools/breaklock/src/controllers/option/option.scss +++ /dev/null @@ -1,28 +0,0 @@ -@import "../../_variables.scss"; - -.selectbox { - border: 1px solid $theme_bright; - margin-bottom: .5rem; - border-radius: .4rem; - padding: .25rem; -} - -.selectbox-item { - @extend %button-feel; - padding: .25rem; - text-align: center; - display: inline-block; - box-sizing: border-box; -} - -.selectbox-item.active { - background: $theme_bright; - color: $theme_dark; - border-radius: .2rem; -} - -.selectbox-1 .selectbox-item { width: 100%; } -.selectbox-2 .selectbox-item { width: 50%; } -.selectbox-3 .selectbox-item { width: 33.33%; } -.selectbox-4 .selectbox-item { width: 25%; } -.selectbox-5 .selectbox-item { width: 20%; } diff --git a/tools/breaklock/src/controllers/readme.md b/tools/breaklock/src/controllers/readme.md deleted file mode 100644 index d3c0daa..0000000 --- a/tools/breaklock/src/controllers/readme.md +++ /dev/null @@ -1,29 +0,0 @@ -# Components - -No framework has been used to make the app as lightweight as possible. However there's a lot of constraints. It's a bit more complex to extend, devs must be confortable with the DOM API, respect a structure. - -Because this project is quite small, it was possible. - -## Contructor - -Every contructor will require different parameters, depending on what's required to build the component. But in every case, the constructor will build the DOM of the component. - -## Properties - -### `.el` [DOM Element] - -This is the root DOM element of your component. It can be used to append your component anywhere else. - -## Methods - -### `.setupTemplate()` - -Build the DOM of the component and set it to the `.el` property. - -### `.init()` - -Start to listen to required events. This method must be called manually, it's not triggered by default. - -### `.destroy()` - -Stop listening to events, delete the DOM and remove any link that could block the garbage collector to clean this up. However this method is not used at any point in this project. diff --git a/tools/breaklock/src/controllers/selector/selector.ctrl.js b/tools/breaklock/src/controllers/selector/selector.ctrl.js deleted file mode 100644 index 4838fe5..0000000 --- a/tools/breaklock/src/controllers/selector/selector.ctrl.js +++ /dev/null @@ -1,130 +0,0 @@ -import dom from '../../utils/dom' - -require('./selector.scss'); - -/** - * Selector Controller - * Component to build a one line selector - * between different options via arrows - * The class used for the parent element - * is .selector, all childs are .selector-item - */ -class SelectorCtrl { - - /** - * Setup the template and different options - * See `setChoices` method to understand the - * format of the following parameters - * @param {Array} choiceList List of key/values to display - */ - constructor (choiceList, defaultChoice) { - this.selectionIndex = 0 - this.setupTemplate() - this.setChoices(choiceList) - } - - /** - * Build template of the controller - * @return {DOMElement} - */ - setupTemplate () { - this.btnLeft = dom.create('span', 'selectbox-item active selector-left', '<') - this.btnRight = dom.create('span', 'selectbox-item active selector-right', '>') - this.labelEl = dom.create('span', 'selectbox-item selector-label') - - this.el = dom.create('div', 'selector selectbox', [ - this.btnLeft, - this.btnRight, - this.labelEl - ]) - return this.el - } - - /** - * Set up listeners - */ - init () { - this.btnLeft.addEventListener('click', this.previous.bind(this)) - this.btnLeft.addEventListener('touchstart', this.previous.bind(this)) - this.btnRight.addEventListener('click', this.next.bind(this)) - this.btnRight.addEventListener('touchstart', this.next.bind(this)) - } - - /** - * Set up the different available choices - * Choice list format: - * [ - * { value: , label: , default: }, - * { value: 2, label: 'Easy'}, - * { value: 3, label: 'Medium', default: true}, - * { value: 4, label: 'Hard'}, - * ] - * @param {Array} choiceList List of options to display - */ - setChoices (choiceList) { - this.choices = choiceList - for (let i = this.choices.length - 1; i >= 0; i--) { - this.selectionIndex = this.choices[i].default ? i : this.selectionIndex - } - this.selectionIndex = this.selectionIndex || 0 - this.updateLabel() - } - - /** - * Update the counter DOM to the value - * set in the instance - * @return {int} The new value of the counter - */ - updateLabel () { - this.selectionIndex = (this.selectionIndex + this.choices.length) % this.choices.length - let choice = this.choices[this.selectionIndex] - this.labelEl.textContent = choice.label - if (this.selectCallback) - this.selectCallback(this.choices[this.selectionIndex]) - return this.selectionIndex - } - - /** - * Decrement the counter - * @return {int} The new value of the counter - */ - next (e) { - e.preventDefault() - e.stopPropagation() - this.selectionIndex++ - return this.updateLabel() - } - - /** - * Increment the counter - * @return {int} The new value of the counter - */ - previous (e) { - e.preventDefault() - e.stopPropagation() - this.selectionIndex-- - return this.updateLabel() - } - - /** - * Listener for when a new item is selected - * The listener will be called with only one - * parameter: the new selected value - * @param {function} listener Select listener to set - */ - onSelect (listener) { - this.selectCallback = listener - this.updateLabel() - } - - /** - * Return the current choice selected - * @return {int} - */ - getValue () { - let choice = this.choices[this.selectionIndex] - return choice && choice.value - } -} - -export default SelectorCtrl diff --git a/tools/breaklock/src/controllers/selector/selector.scss b/tools/breaklock/src/controllers/selector/selector.scss deleted file mode 100644 index 24f4662..0000000 --- a/tools/breaklock/src/controllers/selector/selector.scss +++ /dev/null @@ -1,18 +0,0 @@ -@import "../../_variables.scss"; - -.selector-left, .selector-right { - width: 2rem; -} - -.selector-left { - float: left; -} - -.selector-right { - float: right; -} - -.selector-label { - margin: 0 auto; - display: block; -} diff --git a/tools/breaklock/src/controllers/statusBar/statusBar.ctrl.js b/tools/breaklock/src/controllers/statusBar/statusBar.ctrl.js deleted file mode 100644 index 26bc076..0000000 --- a/tools/breaklock/src/controllers/statusBar/statusBar.ctrl.js +++ /dev/null @@ -1,129 +0,0 @@ -import CountdownCtrl from '../countdown/countdown.ctrl' -import leftPadNum from '../../utils/leftPadNum' -import dom from '../../utils/dom' - -require('./statusBar.scss'); - -/** - * Status Bar Controller - * Status bar of the current game in progress. - * It allows to display 2 different modes: - * - counter: - * showing only an integer that can be - * incremented or decremented - * - countdown: - * showing a progress bar as a timer - * The class used for the parent element - * is .status-bar - */ -class StatusBarCtrl { - - /** - * Constructor - * The only callback provided is for cancelling - * event from the user. - * @param {function} onCancel Cancel callback - */ - constructor (onCancel) { - this.cancelCallback = onCancel - this.counterVal = null - this.setupTemplate() - } - - /** - * Build template of the controller - * @return {DOMElement} - */ - setupTemplate () { - this.cancelBtnEl = dom.create('button', 'status-bar-cancel', 'ABORT') - this.counterEl = dom.create('span', 'status-bar-info') - - this.countdown = new CountdownCtrl() - this.countdownEl = this.countdown.el - this.countdownEl.setAttribute('class', 'status-bar-info') - - this.el = dom.create('div', 'status-bar', [ - this.cancelBtnEl, - this.counterEl, - this.countdownEl - ]) - return this.el - } - - /** - * Set up listeners - */ - init () { - this.cancelBtnEl.addEventListener('click', () => { - this.cancelCallback(0) - }) - } - - /* Counter mode ******************************/ - - /** - * Set the counter mode to the status bar. - * The count is the value displayed on the - * counter. - * @param {int} attemptCount Counter value to display - */ - setCounter (count) { - this.counterEl.style.display = 'inherit' - this.countdownEl.style.display = 'none' - this.counterVal = count - this.updateCounter() - } - - /** - * Update the counter DOM to the value - * set in the instance - * @return {int} The new value of the counter - */ - updateCounter () { - this.counterEl.textContent = leftPadNum(this.counterVal, 3) - return this.counterVal - } - - /** - * Decrement the counter - * @return {int} The new value of the counter - */ - decrementCounter () { - this.counterVal-- - return this.updateCounter() - } - - /** - * Increment the counter - * @return {int} The new value of the counter - */ - incrementCounter () { - this.counterVal++ - return this.updateCounter() - } - - /* Countdown mode ****************************/ - - /** - * Set the countdown mode to the status bar - * @param {int} duration Duration in seconds - */ - setCountdown (duration) { - this.counterEl.style.display = 'none' - this.countdownEl.style.display = 'inherit' - this.countdown.setTimer(duration, () => { - this.cancelCallback(1) - }) - this.countdown.start() - } - - /** - * Stops the countdown - * - */ - stopCountdown () { - this.countdown.stop() - } -} - -export default StatusBarCtrl diff --git a/tools/breaklock/src/controllers/statusBar/statusBar.scss b/tools/breaklock/src/controllers/statusBar/statusBar.scss deleted file mode 100644 index 70bd9f8..0000000 --- a/tools/breaklock/src/controllers/statusBar/statusBar.scss +++ /dev/null @@ -1,26 +0,0 @@ -@import "../../_variables.scss"; - -.status-bar { - height: 1.5rem; - padding-bottom: .25rem; - border-bottom: 1px dotted $theme_bright; - - &:after { - content: ''; - clear: both; - } - - &-cancel { - height: 100%; - font-size: 1rem; - - &:before { - content: '«'; - margin-right: .25em; - } - } - - &-info { - float: right; - } -} diff --git a/tools/breaklock/src/controllers/summary/summary.ctrl.js b/tools/breaklock/src/controllers/summary/summary.ctrl.js deleted file mode 100644 index 479ec43..0000000 --- a/tools/breaklock/src/controllers/summary/summary.ctrl.js +++ /dev/null @@ -1,142 +0,0 @@ -import summaryFeedback from './summaryFeedback' -import config from '../../config' -import Pattern from '../../models/pattern' -import PatternSVG from '../../utils/patternSVG' -import dom from '../../utils/dom' -import airportText from '../../utils/airportText' - -require('./summary.scss'); - -/** - * Summary Controller - * End of game screen. The one that tell - * if the game was a success or not. - * It provide different actions and social - * button to promote the game. - */ -class SummaryCtrl { - - /** - * Set up the template and init event. - * The constructor take one parameter, the callback - * for the following step. - * @param {function} onAction Action callback - */ - constructor (onAction) { - this.onAction = onAction - this.setupTemplate() - this.init() - } - - /** - * Build template of the controller - * @return {DOMElement} - */ - setupTemplate () { - - // Action buttons - this.actionButtons = [] - for (let action in config.GAME.ACTIONS) { - let btn = dom.create('button', { - class: 'summary-action-button', - rel: config.GAME.ACTIONS[action] - }, [ - dom.icon(action.toLowerCase()), - dom.create('span', {}, action) - ]) - this.actionButtons.push(btn) - } - - // Social links - this.socialButtons = [] - for (let platform in config.SOCIAL.PLATFORMS) { - let btn = dom.create('a', { - class: 'summary-share-link', - rel: 'noopener noreferrer', - target: '_blank', - platform - }, [dom.icon(config.SOCIAL.PLATFORMS[platform].ICON)]) - this.socialButtons.push(btn) - } - - // Feedback stuff - let feedbackEl = dom.create('div', 'summary-feedback bloc', [ - dom.create('p', {}, [ - dom.create('span', {}, 'Tweet me your feedback at '), - dom.create('a', {href: config.SOCIAL.PLATFORMS.TWITTER.URL('', '@mxwllt', ['breaklock'])}, '@mxwllt') - ]) - ]) - - this.titleEl = dom.create('h1', 'summary-title highlight') - this.detailsEl = dom.create('p', 'summary-details') - this.revealEl = dom.create('p', 'summary-reveal', 'Continue to see the solution.') - this.actionsEl = dom.create('div', 'summary-actions bloc', this.actionButtons) - this.socialEl = dom.create('div', 'summary-share bloc', this.socialButtons) - - this.el = dom.create('div', 'summary view', [ - dom.create('div', 'view-bloc', [this.titleEl, this.detailsEl, this.revealEl]), - dom.create('div', 'view-bloc', [this.actionsEl, this.socialEl, feedbackEl]) - ]) - - return this.el - } - - /** - * Set up listeners - */ - init () { - this.actionButtons.forEach(btn => btn.addEventListener('click', this.triggerAction.bind(this))) - } - - /** - * Set new content. - * This is independent from the constructor, - * because an instance must be reused. - * @param {Boolean} isSuccess Was the game a success? - * @param {Number} attemptsCount Message to display - */ - setContent (isSuccess, attemptsCount) { - this.titleEl.classList.remove('fail') - this.titleEl.classList.remove('success') - this.titleEl.classList.add(isSuccess ? 'success' : 'fail') - airportText(this.titleEl, isSuccess ? 'Success!' : 'Fail!') - - this.detailsEl.textContent = summaryFeedback(isSuccess, attemptsCount) - this.revealEl.classList[isSuccess ? 'add' : 'remove']('hide') - - this.updateSocialLinks() - this.toggle(true) - } - - /** - * Show/hide the controller - * @param {Boolean} force Force to show or hide if provided - */ - toggle (force) { - force = (force != undefined) ? force : !this.el.classList.contains('active') - this.el.classList[force ? 'add' : 'remove']('active') - } - - /** - * Click listener for action buttons - * @param {Event} event Click event from action button - */ - triggerAction (event) { - let actionId = parseInt(event.currentTarget.getAttribute('rel') || 0, 10) - this.onAction(actionId) - } - - /** - * Update social links from the current content set - */ - updateSocialLinks () { - this.socialButtons.forEach(item => { - let socialId = item.getAttribute('platform'), - socialObj = config.SOCIAL.PLATFORMS[socialId] - - item.setAttribute('href', socialObj.URL(config.URL, config.SOCIAL.MESSAGE, config.SOCIAL.TAGS)) - }) - } -} - -export default SummaryCtrl diff --git a/tools/breaklock/src/controllers/summary/summary.scss b/tools/breaklock/src/controllers/summary/summary.scss deleted file mode 100644 index 47d0643..0000000 --- a/tools/breaklock/src/controllers/summary/summary.scss +++ /dev/null @@ -1,76 +0,0 @@ -@import "../../_variables.scss"; - -$summary_transition_duration: .5s; -$summary_transition_delay: .5s; - -.summary { - position: fixed; - top: 0; - bottom: auto; - left: 0; - right: 0; - height: 0; - overflow: hidden; - background: $theme_dark; - visibility: hidden; - transition: visibility 0s linear $summary_transition_duration, height $summary_transition_duration ease; - -webkit-transition: visibility 0s linear $summary_transition_duration, height $summary_transition_duration ease; - - body.deepblack & { - background-color: #000; - } - - &.active { - visibility: visible; - height: 100%; - transition: visibility 0s, height $summary_transition_duration ease $summary_transition_delay; - -webkit-transition: visibility 0s, height $summary_transition_duration ease $summary_transition_delay; - } - - // Hacks :-/ - &.view { - padding: 0; - - .view-bloc { - padding: $gutter; - } - } -} - -.summary-title { - margin: 0; - - &.success { - color: $theme_bright; - background-color: $theme_success; - } - &.fail { - color: $theme_bright; - background-color: $theme_error; - } -} - -.summary-action-button > * { - vertical-align: middle; - margin-right: .5em; -} -.summary-action-button { - display: block; - font-size: 1.5rem; -} - -.summary-share { - font-size: 2rem; - line-height: 0; - - &-link { - display: inline-block; - margin-right: .375em; - } -} - -@media (orientation: landscape), (min-width: $mq-xlarge-min) { - .summary { - align-items: center; - } -} diff --git a/tools/breaklock/src/controllers/summary/summaryFeedback.js b/tools/breaklock/src/controllers/summary/summaryFeedback.js deleted file mode 100644 index 4d4bbbc..0000000 --- a/tools/breaklock/src/controllers/summary/summaryFeedback.js +++ /dev/null @@ -1,56 +0,0 @@ -const SUCCESS_QUOTES_LIST = [ - { min: 1, max: 3, text: 'That was pure luck, nothing else. Stop dreamin.'}, - { min: 2, max: 4, text: 'You got lucky, without staying up all night.'}, - { min: 1, max: 2, text: 'No merit. Absolutely none.'}, - { min: 2, max: 5, text: 'That was given on a golden plate.'}, - { min: 1, max: 4, text: 'Absolutely no synapse got used during that game.'}, - { min: 2, max: 5, text: 'Don\'t even dare to tweet your score.'}, - { min: 8, max: 10, text: 'Saperlipopette!! That was close.'}, - { min: 4, max: 8, text: 'Seems legit, with a bit of luck.'}, - { min: 7, max: 10, text: 'Pretty good!'}, - { min: 9, max: 10, text: 'But you made it!'}, - { min: 11, max: 50, text: 'Trying random patterns is not a strategy...'}, - { min: 11, max: 50, text: 'That was looooooooong.'}, - { min: 11, max: 50, text: 'At least you made it.'}, - { min: 11, max: 50, text: 'You must hate this game by now.'}, - { min: 11, max: 50, text: 'I hope you didn\'t cheat.'}, - { min: 41, max: 403, text: 'Your dedication is impressive.'}, - { min: 404, max: 404, text: 'Logic not found.'}, - { min: 405, max: 999, text: 'No comment.'} -] - -const FAIL_QUOTES_LIST = [ - 'I believe there\'s some work to do.', - 'Do you understand the game? Don\'t take it personnaly, I struggle to explain it.', - 'One day you will make it...', - 'It\'s not funny for you, but it is for me ;)', - 'Don\'t stress, you will make it.', - 'If you want to avoid battles, stay out of the grassy areas!', - 'Even if you loose in battle, if you surpass what you\'ve done before, you have bested yourself.', - 'TILT! Insert coin and try again!' -] - -/** - * Get a random quote to provide an awful - * feedback to the player. - * @param {boolean} wasSuccess Type of quote to get - * @param {number} attemptsCount Quote score - * @return {string} - */ -function getQuote (wasSuccess, attemptsCount) { - let feedback, matches - if (wasSuccess) { - feedback = `Lock found in ${attemptsCount} attempts. ` - matches = SUCCESS_QUOTES_LIST - .filter(quote => (quote.min <= attemptsCount && quote.max >= attemptsCount)) - .map(quote => quote.text) - } - else { - feedback = 'Sorry, you didn\'t make it this time. ' - matches = FAIL_QUOTES_LIST - } - - return feedback + matches[Math.floor(matches.length * Math.random())] -} - -export default getQuote diff --git a/tools/breaklock/src/models/pattern.js b/tools/breaklock/src/models/pattern.js deleted file mode 100644 index 02650eb..0000000 --- a/tools/breaklock/src/models/pattern.js +++ /dev/null @@ -1,112 +0,0 @@ -/** - * Pattern class - * a pattern is a object representation of a combinaison. - * The amount of dot is specified in the constructor, it's - * not linked to the class itself. - * - * For reference: - * 0 1 2 - * 3 4 5 - * 6 7 8 - * - */ -class Pattern { - - /** - * Set up a pattern with only - * the length of dots to link - * @param {Number} dotLength Length of the pattern - */ - constructor (dotLength) { - this.dotLength = dotLength - this.suite = [] - } - - /** - * Fill the current instance with random values - */ - fillRandomly () { - while (!this.isComplete()) { - this.addDot(Math.floor(Math.random() * 9)) - } - } - - /** - * Add point to the current pattern - * @param {int} dotIndex Dot index to add - * @return boolean True if successfully added - */ - addDot (dotIndex) { - // Test if the dot can be added - if (this.isComplete() || ~this.suite.indexOf(dotIndex)) - return []; - - // Test for potential median dot - let lastDot = this.suite[this.suite.length - 1], - medianDot = (lastDot + dotIndex) / 2 - - if (lastDot != undefined && - medianDot >> 0 === medianDot && - (lastDot%3) - (medianDot%3) === (medianDot%3) - (dotIndex%3) && - Math.floor(lastDot/3) - Math.floor(medianDot/3) === Math.floor(medianDot/3) - Math.floor(dotIndex/3)) { - let addedPoints = this.addDot(medianDot) - if (!this.isComplete()) { - this.suite.push(dotIndex) - addedPoints.push(dotIndex) - } - return addedPoints - } - - this.suite.push(dotIndex) - return [dotIndex] - } - - /** - * Checks if the instance suite is complete - * @return {Boolean} - */ - isComplete () { - return this.suite.length >= this.dotLength - } - - /** - * Checks if a dot is already in the pattern - * @param {int} dotIndex Index to check - * @return {boolean} - */ - gotDot (dotIndex) { - return ~this.suite.indexOf(dotIndex) - } - - /** - * Compare a pattern with the current instance. - * The output will be an array of three values: - * [0]: Number of dots in the right place in the pattern - * [1]: Number of correct dots badly placed in the pattern - * [2]: Number of wrong dots - * @param {Pattern} pattern Pattern to compare - * @return {Array} - */ - compare (pattern) { - var goodPos = 0, - wrongPos = 0 - for (let i = 0; i < this.dotLength; i++) { - if (this.suite[i] === pattern.suite[i]) - goodPos++ - for (let j = 0; j < this.dotLength; j++) { - if (this.suite[j] === pattern.suite[i]) - wrongPos++ - } - } - return [goodPos, wrongPos - goodPos, this.dotLength - wrongPos] - } - - /** - * Reset the pattern by removing all the dots - */ - reset () { - this.suite = [] - } -} - -export default Pattern diff --git a/tools/breaklock/src/style.scss b/tools/breaklock/src/style.scss deleted file mode 100644 index 60d9f6e..0000000 --- a/tools/breaklock/src/style.scss +++ /dev/null @@ -1,186 +0,0 @@ -@import "./_variables.scss"; - -@font-face { - font-family: "Roboto Mono"; - font-weight: $font_weight; - src: url('assets/fonts/robotomono-light-webfont.woff2') format('woff2'), - url('assets/fonts/robotomono-light-webfont.woff') format('woff'), - url('assets/fonts/robotomono-light-webfont.ttf') format('truetype'); -} - -* { - font-family: 'Roboto Mono', Menlo, Consolas, 'Andale Mono', monospace; - font-weight: $font_weight; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -html, body { - height: 100%; -} -body { - position: relative; - margin: 0 auto; - padding: $gutter; - max-width: 1200px; - box-sizing: border-box; - background: $theme_dark; - color: $theme_bright; - overscroll-behavior-y: none; -} -body.deepblack { - background-color: #000; -} - -a { - color: inherit; -} - -button { - @extend %button-feel; - font-weight: $font_weight; - background: none; - color: $theme_bright; - line-height: inherit; - font-size: inherit; - border: none; - padding: 0; -} - -h1, h2, h3, h4, h5, h6 { - font-weight: $font_weight; -} - -table, caption, tbody, tfoot, thead, tr, th, td { - margin: 0; - padding: 0; - border: 0; - font-size: 100%; - font: inherit; - vertical-align: middle; -} - -table { - border-collapse: collapse; - border-spacing: 0; -} - -.icon { - width: 1em; - height: 1em; - vertical-align: middle; - - * { - fill: currentColor; - } -} - -::-webkit-scrollbar { - display: none; -} - -*:focus { - outline: none; -} - -.bloc { - border-top: 1px dotted currentColor; - padding: .75rem 0; -} - -.action-btn { - display: block; - text-align: center; - margin: 0 auto .25rem; - font-size: 1.25rem; - border: 1px solid $theme_bright; - padding: .25rem 1rem; - box-shadow: .25rem .25rem 0 0 $theme_bright; - border-radius: .4rem; -} - -.highlight { - display: inline-block; - padding: 0 .5rem; - color: $theme_dark; - background-color: $theme_bright; - &:after { - content:'_'; - animation: blink 1.2s infinite; - -webkit-animation: blink 1.2s infinite; - } -} - -.hide { - display: none; -} - -.unselectable { - -webkit-touch-callout: none; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} - -/* Layout *********/ -.view { - display: flex; - flex-direction: column; - justify-content: space-between; - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; - padding: $gutter; - overflow: scroll; -} - -.view-bloc { - flex-grow: 1; -} - - -@media (orientation: landscape), (min-width: $mq-xlarge-min) { - .view { - flex-direction: row; - } - .view-bloc { - flex-basis: 50%; - max-height: 100%; - overflow-y: scroll; - & + & { - margin-left: $gutter/2; - } - } -} - -@media (max-width: 320px) { - html { - font-size: 14px; - } -} - - -@media (min-width: $mq-xlarge-min) { - html { - font-size: 20px; - } -} - - -@keyframes blink { - 0% {opacity: 1;} - 50% {opacity: 1;} - 50.01% {opacity: 0;} - 100% {opacity: 0;} -} - -@-webkit-keyframes blink { - 0% {opacity: 1;} - 50% {opacity: 1;} - 50.01% {opacity: 0;} - 100% {opacity: 0;} -} diff --git a/tools/breaklock/src/utils/airportText.js b/tools/breaklock/src/utils/airportText.js deleted file mode 100644 index c800b32..0000000 --- a/tools/breaklock/src/utils/airportText.js +++ /dev/null @@ -1,122 +0,0 @@ -/** - * AirportText - * - * Made to display text with transition - * With an effect of airport display - */ - -var animStack = [], - ratioFrameRate = 3 - -/** - * Set up text animation on a dom element - * @param {DOMElement} element Element to animate - * @param {String} text Text to display in the element - */ -function airportText (element, text) { - let existingAnim = popAnim(element) - if (existingAnim) - cancelFakeNextFrame(existingAnim.nextFrame) - - var newAnim = { - el: element, - counter: text.length * ratioFrameRate, - originalLength: text.length, - originalText: text, - nextFrame: null - } - updateDisplay(newAnim) - - animStack.push(newAnim) -} - -/** - * Find an anim object from an element and extract it - * from the anim stack - * @param {DOMElement} element Element to find - * @return {Object} - */ -function popAnim (element) { - for (let i = animStack.length - 1; i >= 0; i--) { - if (animStack[i].el === element) - return animStack.splice(i, 1)[0] - } -} - -/** - * Set the next for animation - * @param {Object} anim Anim object to set - */ -function setNextFrame (anim) { - anim.nextFrame = requestFakeNextFrame(function () { - updateDisplay(anim) - }) -} - -/** - * Update the text of an anim object - * @param {Object} anim Anim object to update - * @return {[type]} - */ -function updateDisplay (anim) { - - // Increase the counter - anim.counter -= 1; - - if (anim.counter <= 0) { - - // Wait to be ready - anim.el.textContent = anim.originalText - popAnim(anim.el) - return - } - - var randomLength = Math.floor(anim.originalLength - anim.counter / ratioFrameRate); - anim.el.textContent = anim.originalText.substr(0, randomLength) + getRandomWord(Math.min(anim.originalLength - randomLength, 3)); - - setNextFrame(anim) -} - - -/** - * getRandomWord - * Get a random word (in lowercase) - * - * @param pLength int Length of the random word - * @return string Random word - */ -function getRandomWord (pLength) { - var toReturn = ''; - var charList = 'abcdefghijklmnopqrstuvwxyz0123456789 _*%!?/\\|#@'; - - // Param tests - if (pLength <= 0) { - return toReturn; - } - - // Generate word - for (let i = 0; i < pLength; i++) { - toReturn += charList.charAt(Math.floor(Math.random() * charList.length)); - } - - return toReturn; -} - -/** - * Fake mock of requestAnimationFrame - * @param {Function} callback Function to call - * @return {Int} - */ -function requestFakeNextFrame (callback){ - return window.setTimeout(callback, 60); -} - -/** - * Cancel a planed call - * @param {Int} id Process ID of the planed call to execute - */ -function cancelFakeNextFrame (id){ - return window.clearTimeout(id); -} - -export default airportText \ No newline at end of file diff --git a/tools/breaklock/src/utils/color.js b/tools/breaklock/src/utils/color.js deleted file mode 100644 index 7d1e024..0000000 --- a/tools/breaklock/src/utils/color.js +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Set of tools to manipulate colors - */ - -var color = { - - /** - * Create gradients of grey - * Specify the color to start and end with plus - * the amount of intermediate steps. - * The method will return an array of string - * format hex string. - * - * Example: - * greydient('99', 'FF', 2) - * > ['#999999', '#bbbbbb', '#dddddd', '#ffffff'] - * @param {Number|String} colorStart Start gradient color (: '99' or 153) - * @param {Number|String} colorEnd End gradient color (: 'FF' or 255) - * @param {Number} steps Amount of intermediate colors - * @return {Array} Color list - */ - greydient: (colorStart, colorEnd, steps = 0) => { - // Convert to number if hex string - colorStart = typeof colorStart === 'string' ? parseInt(colorStart, 16) : colorStart - colorEnd = typeof colorEnd === 'string' ? parseInt(colorEnd, 16) : colorEnd - - // Linit to the spectrum - colorStart = Math.min(255, Math.max(0, colorStart)) - colorEnd = Math.min(255, Math.max(0, colorEnd)) - - steps++ - let output = [], - gap = (colorEnd - colorStart) / steps - - for (let i = 0; i <= steps; i++) { - let grey = Math.round(colorStart + i * gap), - greyHex = grey.toString(16) - output.push('#' + greyHex + greyHex + greyHex) - } - return output - } -} - -export default color diff --git a/tools/breaklock/src/utils/dom.js b/tools/breaklock/src/utils/dom.js deleted file mode 100644 index 1c9821f..0000000 --- a/tools/breaklock/src/utils/dom.js +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Set of tools to generate quickly DOM elements - */ - -var dom = { - - /** - * Most advanced method (of this object) to - * create a DOM element - * @param {String} nodeName Node type to create - * @param {Object|String} props Key/value of attributes to set - * @param {Array|String} content Text content if string or childnodes if array to include - * @return {DOMElement} Node generated - */ - create: (nodeName, props = {}, content = null) => { - var node - - // Create the node - if (dom.SVG_ELEMENTS.indexOf(nodeName) === -1) - node = document.createElement(nodeName) - else - node = document.createElementNS(dom.SVG_NAMESPACE, nodeName) - - // Set class or attributes - if (props.constructor === String) - node.setAttribute('class', props) - else - for (let propName in props) - node.setAttribute(propName, props[propName]) - - // Set content or child - if (content instanceof Array) - for (let i = 0; i < content.length; i++) { - node.appendChild(content[i]) - } - else - node.textContent = content - - return node - }, - - /** - * Generate SVG icon dom, using 'use' tags - * and the definitions in the index.html - * @param {String} name Icon name (cf. definitions in index.html) - * @return {SVGDOMElement} - */ - icon: (name) => { - let use = dom.create('use') - use.setAttributeNS(dom.XLINK_NAMESPACE, 'href', '#icon-' + name) - return dom.create('svg', {class: 'icon'}, [use]) - }, - - /** - * Clear the content of an element - * @param {DOMElement} element Element to clear - */ - clear: (element) => { - for (let i = element.childNodes.length - 1; i >= 0; i--) { - element.childNodes[i].remove() - } - }, - - SVG_NAMESPACE: 'http://www.w3.org/2000/svg', - XLINK_NAMESPACE: 'http://www.w3.org/1999/xlink', - SVG_ELEMENTS: ['svg', 'g', 'circle', 'line', 'path', 'use', 'rect'] -} - -export default dom diff --git a/tools/breaklock/src/utils/leftPadNum.js b/tools/breaklock/src/utils/leftPadNum.js deleted file mode 100644 index 27c855c..0000000 --- a/tools/breaklock/src/utils/leftPadNum.js +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Dummy leftpad for numbers - * @param {number} num Number to left pad - * @param {number} len Length of the final string - * @return {string} Leftpadded string - */ -var leftPadNum = (num, len) => { - let str = Math.abs(num) + '', - isNegative = num < 0 - for (let i = (len - str.length); i > 0; i--) { - str = '0' + str - } - return (isNegative ? '-' : '') + str -} - -export default leftPadNum diff --git a/tools/breaklock/src/utils/patternSVG.js b/tools/breaklock/src/utils/patternSVG.js deleted file mode 100644 index 4f496a1..0000000 --- a/tools/breaklock/src/utils/patternSVG.js +++ /dev/null @@ -1,154 +0,0 @@ -import dom from './dom' - -/** - * Class to generate SVG from Pattern objects - */ -class PatternSVG { - - /** - * Setup the SVG base node - * - */ - constructor () { - this.el = dom.create('svg', {'viewBox': '0 0 ' + this.SVG_WIDTH + ' ' + this.SVG_WIDTH}) - } - - /** - * Add an invisible rectangle as background. - * This is only to help Safari to catch touch events - * on the SVG lock. - */ - addBackgroundLayer () { - let rect = dom.create('rect', { - 'fill': '#fff', - 'fill-opacity': '0', - 'width': this.SVG_WIDTH, - 'height': this.SVG_WIDTH - }) - this.el.appendChild(rect) - return rect - } - - /** - * Add pattern to the instance - * @param {Pattern} pattern Pattern instance to get the points from - * @param {int} size Thickness of the line - * @param {string|array} color List of colors to use for the pattern - * @return PatternSVG - */ - addPattern (pattern, size = 14, color = '#fff') { - let lines = [] - color = color instanceof Array ? color : [color] - - // Add all lines - for (let i = 1; i < pattern.suite.length; i++) { - lines.push(dom.create('line', { - 'x1': (pattern.suite[i-1] % 3) * this.GRID_GUTTER + this.SVG_MARGIN, - 'y1': Math.floor(pattern.suite[i-1] / 3) * this.GRID_GUTTER + this.SVG_MARGIN, - 'x2': (pattern.suite[i] % 3) * this.GRID_GUTTER + this.SVG_MARGIN, - 'y2': Math.floor(pattern.suite[i] / 3) * this.GRID_GUTTER + this.SVG_MARGIN, - 'stroke': color[Math.min(color.length, i) - 1] - })) - } - - // Add the dot reprenting the final dot - let lastDotIndex = pattern.suite[pattern.suite.length - 1] - lines.push(dom.create('circle', { - cx: (lastDotIndex % 3) * this.GRID_GUTTER + this.SVG_MARGIN, - cy: Math.floor(lastDotIndex / 3) * this.GRID_GUTTER + this.SVG_MARGIN, - fill: color[0], - r: size / 4 - })) - - // Bundle everything in a group - return this.addGroup({ - 'stroke-width': size, - 'stroke-linecap': 'round' - }, lines) - } - - /** - * Add dots to the instance - * @param {int} size Thickness of the line - * @param {object} attr List of attributes to set to the group - * @return PatternSVG - */ - addDots (size = 3, attr = {}) { - let dots = [] - attr.fill = attr.fill || '#fff' - - for (let i = 0; i < 9; i++) { - dots.push(dom.create('circle', { - cx: (i % 3) * this.GRID_GUTTER + this.SVG_MARGIN, - cy: Math.floor(i / 3) * this.GRID_GUTTER + this.SVG_MARGIN, - rel: i, - r: size - })) - } - return this.addGroup(attr, dots) - } - - /** - * Add group to the instance - * @param {object} attr List of attributes to set to the group - * @param {*} content Items as content - * @return SVGDOMElement - */ - addGroup (attr, content) { - let group = dom.create('g', attr, content) - this.el.appendChild(group) - return group - } - - /** - * Add combinaison results - * @param {int} goodDots Amount of good dots - * @param {int} badPlacedDots Amount of badly placed dots - * @param {int} wrongDots Amount of wrong dots - */ - addCombinaison (goodDots, badPlacedDots, wrongDots) { - let totalDots = goodDots + badPlacedDots + wrongDots, - dot = Math.min(Math.floor(this.SVG_WIDTH / totalDots), this.SVG_COMB_EXP), - dotWidth = Math.floor(dot * .75), - dotGap = Math.floor(dot * .25), - xGap = dotWidth + dotGap, - xStart = Math.floor((this.SVG_WIDTH - (totalDots - 1) * xGap) / 2), - yStart = this.SVG_WIDTH + Math.floor(this.SVG_COMB_EXP / 2) - - - this.el.setAttribute('viewBox', '0 0 ' + this.SVG_WIDTH + ' ' + (this.SVG_WIDTH + this.SVG_COMB_EXP)) - let dots = [] - - for (let i = 0; i < totalDots; i++) { - dots.push(dom.create('circle', { - 'cx': xStart + i * xGap, - 'cy': yStart, - 'r': (dotWidth - this.DOT_BORDER) / 2, - 'stroke-width': this.DOT_BORDER, - 'fill': i < goodDots ? '#fff' : '#000', - 'stroke': i < (goodDots + badPlacedDots) ? '#fff' : '#000', - 'fill-opacity': i < goodDots ? '1' : '.25', - 'stroke-opacity': i < (goodDots + badPlacedDots) ? '1' : '.25' - })) - } - return this.addGroup({}, dots) - } - - /** - * Get the SVG - * @return SVGDOMElement - */ - getSVG () { - return this.el - } -} - -PatternSVG.prototype.SVG_NAMESPACE = 'http://www.w3.org/2000/svg' -PatternSVG.prototype.SVG_WIDTH = 100 -PatternSVG.prototype.SVG_COMB_EXP = 20 -PatternSVG.prototype.SVG_MARGIN = 15 -PatternSVG.prototype.GRID_GUTTER = 35 -PatternSVG.prototype.DOT_BORDER = 2 -PatternSVG.prototype.DOT_MAGNET = 6 - -export default PatternSVG diff --git a/tools/breaklock/src/utils/polyfill.js b/tools/breaklock/src/utils/polyfill.js deleted file mode 100644 index 002ed74..0000000 --- a/tools/breaklock/src/utils/polyfill.js +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Alias for `requestAnimationFrame` or - * `setTimeout` function for deprecated browsers. - * - */ -window.requestAnimationFrame = (function () { - return ( - window.requestAnimationFrame || - window.webkitRequestAnimationFrame || - window.mozRequestAnimationFrame || - window.oRequestAnimationFrame || - window.msRequestAnimationFrame || - function(/* function */ callback){ - return window.setTimeout(callback, 1000 / 60); - } - ); -})(); - -/** - * Alias for `cancelAnimationFrame` or - * `cancelTimeout` function for deprecated browsers. - * - */ -window.cancelAnimationFrame = (function () { - return ( - window.cancelAnimationFrame || - window.webkitCancelAnimationFrame || - window.mozCancelAnimationFrame || - window.oCancelAnimationFrame || - window.msCancelAnimationFrame || - function(id){ - return window.clearTimeout(id); - } - ); -})(); - -var toExport = { - requestAnimationFrame: window.requestAnimationFrame, - cancelAnimationFrame: window.cancelAnimationFrame -} - -export default toExport diff --git a/tools/breaklock/webpack.config.js b/tools/breaklock/webpack.config.js deleted file mode 100644 index 8ab2fa6..0000000 --- a/tools/breaklock/webpack.config.js +++ /dev/null @@ -1,45 +0,0 @@ -const path = require('path'); -const webpack = require('webpack'); -const ExtractTextPlugin = require("extract-text-webpack-plugin"); - -const extractSass = new ExtractTextPlugin({ - filename: "[name].css", - disable: process.env.NODE_ENV === "development" -}); - -module.exports = { - context: path.resolve(__dirname, './src'), - entry: { - app: './app.js', - }, - output: { - path: path.resolve(__dirname, './'), - filename: 'app.js', - }, - module: { - rules: [ - { - test: /\.js$/, - use: [{ - loader: 'babel-loader', - options: { presets: ['es2015'] } - }], - }, - { - test: /\.scss$/, - loader: extractSass.extract({ - loader: [{ - loader: "css-loader", - options: { url: false } - }, { - loader: "sass-loader" - }], - fallbackLoader: "style-loader" - }) - } - ] - }, - plugins: [ - extractSass - ] -};