2?ga.call(arguments,2):[];return M(a)&&!(a instanceof RegExp)?c.length?
12 | function(){return arguments.length?a.apply(b,c.concat(ga.call(arguments,0))):a.apply(b,c)}:function(){return arguments.length?a.apply(b,arguments):a.call(b)}:a}function hc(b,a){var c=a;/^\$+/.test(b)?c=p:na(a)?c="$WINDOW":a&&aa===a?c="$DOCUMENT":a&&a.$evalAsync&&a.$watch&&(c="$SCOPE");return c}function ba(b,a){return JSON.stringify(b,hc,a?" ":null)}function mb(b){return G(b)?JSON.parse(b):b}function Wa(b){b&&b.length!==0?(b=C(""+b),b=!(b=="f"||b=="0"||b=="false"||b=="no"||b=="n"||b=="[]")):b=!1;
13 | return b}function oa(b){b=u(b).clone();try{b.html("")}catch(a){}return u("").append(b).html().match(/^(<[^>]+>)/)[1].replace(/^<([\w\-]+)/,function(a,b){return"<"+C(b)})}function Xa(b){var a={},c,d;m((b||"").split("&"),function(b){b&&(c=b.split("="),d=decodeURIComponent(c[0]),a[d]=s(c[1])?decodeURIComponent(c[1]):!0)});return a}function nb(b){var a=[];m(b,function(b,d){a.push(Ya(d,!0)+(b===!0?"":"="+Ya(b,!0)))});return a.length?a.join("&"):""}function Za(b){return Ya(b,!0).replace(/%26/gi,"&").replace(/%3D/gi,
14 | "=").replace(/%2B/gi,"+")}function Ya(b,a){return encodeURIComponent(b).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(a?null:/%20/g,"+")}function ic(b,a){function c(a){a&&d.push(a)}var d=[b],e,g,h=["ng:app","ng-app","x-ng-app","data-ng-app"],f=/\sng[:\-]app(:\s*([\w\d_]+);?)?\s/;m(h,function(a){h[a]=!0;c(aa.getElementById(a));a=a.replace(":","\\:");b.querySelectorAll&&(m(b.querySelectorAll("."+a),c),m(b.querySelectorAll("."+a+"\\:"),c),m(b.querySelectorAll("["+
15 | a+"]"),c))});m(d,function(a){if(!e){var b=f.exec(" "+a.className+" ");b?(e=a,g=(b[2]||"").replace(/\s+/g,",")):m(a.attributes,function(b){if(!e&&h[b.name])e=a,g=b.value})}});e&&a(e,g?[g]:[])}function ob(b,a){b=u(b);a=a||[];a.unshift(["$provide",function(a){a.value("$rootElement",b)}]);a.unshift("ng");var c=pb(a);c.invoke(["$rootScope","$rootElement","$compile","$injector",function(a,b,c,h){a.$apply(function(){b.data("$injector",h);c(b)(a)})}]);return c}function $a(b,a){a=a||"_";return b.replace(jc,
16 | function(b,d){return(d?a:"")+b.toLowerCase()})}function pa(b,a,c){if(!b)throw new z("Argument '"+(a||"?")+"' is "+(c||"required"));return b}function qa(b,a,c){c&&K(b)&&(b=b[b.length-1]);pa(M(b),a,"not a function, got "+(b&&typeof b=="object"?b.constructor.name||"Object":typeof b));return b}function kc(b){function a(a,b,e){return a[b]||(a[b]=e())}return a(a(b,"angular",Object),"module",function(){var b={};return function(d,e,g){e&&b.hasOwnProperty(d)&&(b[d]=null);return a(b,d,function(){function a(c,
17 | d,e){return function(){b[e||"push"]([c,d,arguments]);return k}}if(!e)throw z("No module: "+d);var b=[],c=[],j=a("$injector","invoke"),k={_invokeQueue:b,_runBlocks:c,requires:e,name:d,provider:a("$provide","provider"),factory:a("$provide","factory"),service:a("$provide","service"),value:a("$provide","value"),constant:a("$provide","constant","unshift"),filter:a("$filterProvider","register"),controller:a("$controllerProvider","register"),directive:a("$compileProvider","directive"),config:j,run:function(a){c.push(a);
18 | return this}};g&&j(g);return k})}})}function qb(b){return b.replace(lc,function(a,b,d,e){return e?d.toUpperCase():d}).replace(mc,"Moz$1")}function ab(b,a){function c(){var e;for(var b=[this],c=a,h,f,i,j,k,l,n;b.length;){h=b.shift();f=0;for(i=h.length;f
"+b;a.removeChild(a.firstChild);bb(this,a.childNodes);this.remove()}else bb(this,b)}function cb(b){return b.cloneNode(!0)}function ra(b){rb(b);for(var a=0,b=b.childNodes||[];a
21 | -1}function vb(b,a){a&&m(a.split(" "),function(a){b.className=Q((" "+b.className+" ").replace(/[\n\t]/g," ").replace(" "+Q(a)+" "," "))})}function wb(b,a){a&&m(a.split(" "),function(a){if(!Ca(b,a))b.className=Q(b.className+" "+Q(a))})}function bb(b,a){if(a)for(var a=!a.nodeName&&s(a.length)&&!na(a)?a:[a],c=0;c4096&&c.warn("Cookie '"+a+"' possibly not set or overflowed because it was too large ("+d+" > 4096 bytes)!"),V.length>20&&c.warn("Cookie '"+a+"' possibly not set or overflowed because too many cookies were already set ("+
33 | V.length+" > 20 )")}else{if(i.cookie!==I){I=i.cookie;d=I.split("; ");V={};for(f=0;f0&&(V[unescape(e.substring(0,g))]=unescape(e.substring(g+1)))}return V}};f.defer=function(a,b){var c;o++;c=l(function(){delete r[c];e(a)},b||0);r[c]=!0;return c};f.defer.cancel=function(a){return r[a]?(delete r[a],n(a),e(x),!0):!1}}function vc(){this.$get=["$window","$log","$sniffer","$document",function(b,a,c,d){return new uc(b,d,a,c)}]}function wc(){this.$get=function(){function b(b,
34 | d){function e(a){if(a!=l){if(n){if(n==a)n=a.n}else n=a;g(a.n,a.p);g(a,l);l=a;l.n=null}}function g(a,b){if(a!=b){if(a)a.p=b;if(b)b.n=a}}if(b in a)throw z("cacheId "+b+" taken");var h=0,f=D({},d,{id:b}),i={},j=d&&d.capacity||Number.MAX_VALUE,k={},l=null,n=null;return a[b]={put:function(a,b){var c=k[a]||(k[a]={key:a});e(c);v(b)||(a in i||h++,i[a]=b,h>j&&this.remove(n.key))},get:function(a){var b=k[a];if(b)return e(b),i[a]},remove:function(a){var b=k[a];if(b==l)l=b.p;if(b==n)n=b.n;g(b.n,b.p);delete k[a];
35 | delete i[a];h--},removeAll:function(){i={};h=0;k={};l=n=null},destroy:function(){k=f=i=null;delete a[b]},info:function(){return D({},f,{size:h})}}}var a={};b.info=function(){var b={};m(a,function(a,e){b[e]=a.info()});return b};b.get=function(b){return a[b]};return b}}function xc(){this.$get=["$cacheFactory",function(b){return b("templates")}]}function Bb(b){var a={},c="Directive",d=/^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,e=/(([\d\w\-_]+)(?:\:([^;]+))?;?)/,g="Template must have exactly one root element. was: ";
36 | this.directive=function f(d,e){G(d)?(pa(e,"directive"),a.hasOwnProperty(d)||(a[d]=[],b.factory(d+c,["$injector","$exceptionHandler",function(b,c){var e=[];m(a[d],function(a){try{var f=b.invoke(a);if(M(f))f={compile:B(f)};else if(!f.compile&&f.link)f.compile=B(f.link);f.priority=f.priority||0;f.name=f.name||d;f.require=f.require||f.controller&&f.name;f.restrict=f.restrict||"A";e.push(f)}catch(g){c(g)}});return e}])),a[d].push(e)):m(d,lb(f));return this};this.$get=["$injector","$interpolate","$exceptionHandler",
37 | "$http","$templateCache","$parse","$controller","$rootScope",function(b,i,j,k,l,n,r,o){function w(a,b,c){a instanceof u||(a=u(a));m(a,function(b,c){b.nodeType==3&&(a[c]=u(b).wrap("").parent()[0])});var d=t(a,b,a,c);return function(b,c){pa(b,"scope");var e=c?ta.clone.call(a):a;e.data("$scope",b);q(e,"ng-scope");c&&c(e,b);d&&d(b,e,e);return e}}function q(a,b){try{a.addClass(b)}catch(c){}}function t(a,b,c,d){function e(a,c,d,g){for(var j,i,n,k,l,o=0,r=0,q=f.length;oE.priority)break;if(B=E.scope)N("isolated scope",y,E,F),J(B)&&(q(F,"ng-isolate-scope"),y=E),q(F,"ng-scope"),A=A||E;W=E.name;if(B=E.controller)s=s||{},N("'"+W+"' controller",s[W],E,F),s[W]=E;if(B=E.transclude)N("transclusion",x,E,F),x=E,l=E.priority,B=="element"?($=u(b),F=c.$$element=u("<\!-- "+W+": "+c[W]+" --\>"),b=F[0],Ga(e,u($[0]),b),v=w($,d,l)):($=u(cb(b)).contents(),F.html(""),v=w($,d));if(B=E.template)if(N("template",I,E,F),I=E,$=u(""+Q(B)+"
").contents(),b=$[0],E.replace){if($.length!=
44 | 1||b.nodeType!==1)throw new z(g+B);Ga(e,F,b);W={$attr:{}};a=a.concat(X(b,a.splice(C+1,a.length-(C+1)),W));L(c,W);H=a.length}else F.html(B);if(E.templateUrl)N("template",I,E,F),I=E,k=V(a.splice(C,a.length-C),k,F,c,e,E.replace,v),H=a.length;else if(E.compile)try{D=E.compile(F,c,v),M(D)?f(null,D):D&&f(D.pre,D.post)}catch(O){j(O,oa(F))}if(E.terminal)k.terminal=!0,l=Math.max(l,E.priority)}k.scope=A&&A.scope;k.transclude=x&&v;return k}function y(d,e,g,i){var n=!1;if(a.hasOwnProperty(e))for(var k,e=b.get(e+
45 | c),l=0,o=e.length;lk.priority)&&k.restrict.indexOf(g)!=-1)d.push(k),n=!0}catch(r){j(r)}return n}function L(a,b){var c=b.$attr,d=a.$attr,e=a.$$element;m(a,function(d,e){e.charAt(0)!="$"&&(b[e]&&(d+=(e==="style"?";":" ")+b[e]),a.$set(e,d,!0,c[e]))});m(b,function(b,f){f=="class"?(q(e,b),a["class"]=(a["class"]?a["class"]+" ":"")+b):f=="style"?e.attr("style",e.attr("style")+";"+b):f.charAt(0)!="$"&&!a.hasOwnProperty(f)&&(a[f]=b,d[f]=c[f])})}function V(a,b,c,d,e,f,j){var i=
46 | [],n,o,r=c[0],q=a.shift(),w=D({},q,{controller:null,templateUrl:null,transclude:null});c.html("");k.get(q.templateUrl,{cache:l}).success(function(k){var l,q;if(f){q=u(""+Q(k)+"
").contents();l=q[0];if(q.length!=1||l.nodeType!==1)throw new z(g+k);k={$attr:{}};Ga(e,c,l);X(l,a,k);L(d,k)}else l=r,c.html(k);a.unshift(w);n=A(a,c,d,j);for(o=t(c.contents(),j);i.length;){var m=i.pop(),k=i.pop();q=i.pop();var y=i.pop(),I=l;q!==r&&(I=cb(l),Ga(k,u(q),I));n(function(){b(o,y,I,e,m)},y,I,e,m)}i=null}).error(function(a,
47 | b,c,d){throw z("Failed to load template: "+d.url);});return function(a,c,d,e,f){i?(i.push(c),i.push(d),i.push(e),i.push(f)):n(function(){b(o,c,d,e,f)},c,d,e,f)}}function I(a,b){return b.priority-a.priority}function N(a,b,c,d){if(b)throw z("Multiple directives ["+b.name+", "+c.name+"] asking for "+a+" on: "+oa(d));}function F(a,b){var c=i(b,!0);c&&a.push({priority:0,compile:B(function(a,b){var d=b.parent(),e=d.data("$binding")||[];e.push(c);q(d.data("$binding",e),"ng-binding");a.$watch(c,function(a){b[0].nodeValue=
48 | a})})})}function W(a,b,c,d){var e=i(c,!0);e&&b.push({priority:100,compile:B(function(a,b,c){b=c.$$observers||(c.$$observers={});d==="class"&&(e=i(c[d],!0));c[d]=p;(b[d]||(b[d]=[])).$$inter=!0;(c.$$observers&&c.$$observers[d].$$scope||a).$watch(e,function(a){c.$set(d,a)})})})}function Ga(a,b,c){var d=b[0],e=d.parentNode,f,g;if(a){f=0;for(g=a.length;f0){var e=N[0],f=e.text;if(f==a||f==b||f==c||f==d||!a&&!b&&!c&&!d)return e}return!1}function f(b,c,d,f){return(b=h(b,c,d,f))?(a&&!b.json&&e("is not valid json",b),N.shift(),b):!1}function i(a){f(a)||e("is unexpected, expecting ["+
66 | a+"]",h())}function j(a,b){return function(c,d){return a(c,d,b)}}function k(a,b,c){return function(d,f){return b(d,f,a,c)}}function l(){for(var a=[];;)if(N.length>0&&!h("}",")",";","]")&&a.push(v()),!f(";"))return a.length==1?a[0]:function(b,c){for(var d,f=0;f","<=",">="))a=k(a,b.fn,q());return a}function t(){for(var a=m(),b;b=f("*","/","%");)a=k(a,b.fn,m());return a}function m(){var a;return f("+")?A():(a=f("-"))?k(V,a.fn,m()):(a=f("!"))?j(a.fn,m()):A()}function A(){var a;
68 | if(f("("))a=v(),i(")");else if(f("["))a=y();else if(f("{"))a=L();else{var b=f();(a=b.fn)||e("not a primary expression",b)}for(var c;b=f("(","[",".");)b.text==="("?(a=u(a,c),c=null):b.text==="["?(c=a,a=da(a)):b.text==="."?(c=a,a=s(a)):e("IMPOSSIBLE");return a}function y(){var a=[];if(g().text!="]"){do a.push(F());while(f(","))}i("]");return function(b,c){for(var d=[],f=0;f1;d++){var e=a.shift(),g=b[e];g||(g={},b[e]=g);b=g}return b[a.shift()]=c}function eb(b,a,c){if(!a)return b;for(var a=a.split("."),d,e=b,g=a.length,h=0;h7),hasEvent:function(c){if(c=="input"&&Z==9)return!1;if(v(a[c])){var e=b.document.createElement("div");a[c]="on"+c in e}return a[c]},csp:!1}}]}function Tc(){this.$get=B(T)}function Mb(b){var a={},c,d,e;if(!b)return a;m(b.split("\n"),function(b){e=b.indexOf(":");c=C(Q(b.substr(0,e)));
89 | d=Q(b.substr(e+1));c&&(a[c]?a[c]+=", "+d:a[c]=d)});return a}function Nb(b){var a=J(b)?b:p;return function(c){a||(a=Mb(b));return c?a[C(c)]||null:a}}function Ob(b,a,c){if(M(c))return c(b,a);m(c,function(c){b=c(b,a)});return b}function Uc(){var b=/^\s*(\[|\{[^\{])/,a=/[\}\]]\s*$/,c=/^\)\]\}',?\n/,d=this.defaults={transformResponse:[function(d){G(d)&&(d=d.replace(c,""),b.test(d)&&a.test(d)&&(d=mb(d,!0)));return d}],transformRequest:[function(a){return J(a)&&Sa.apply(a)!=="[object File]"?ba(a):a}],headers:{common:{Accept:"application/json, text/plain, */*",
90 | "X-Requested-With":"XMLHttpRequest"},post:{"Content-Type":"application/json;charset=utf-8"},put:{"Content-Type":"application/json;charset=utf-8"}}},e=this.responseInterceptors=[];this.$get=["$httpBackend","$browser","$cacheFactory","$rootScope","$q","$injector",function(a,b,c,i,j,k){function l(a){function c(a){var b=D({},a,{data:Ob(a.data,a.headers,f)});return 200<=a.status&&a.status<300?b:j.reject(b)}a.method=la(a.method);var e=a.transformRequest||d.transformRequest,f=a.transformResponse||d.transformResponse,
91 | g=d.headers,g=D({"X-XSRF-TOKEN":b.cookies()["XSRF-TOKEN"]},g.common,g[C(a.method)],a.headers),e=Ob(a.data,Nb(g),e),i;v(a.data)&&delete g["Content-Type"];i=n(a,e,g);i=i.then(c,c);m(w,function(a){i=a(i)});i.success=function(b){i.then(function(c){b(c.data,c.status,c.headers,a)});return i};i.error=function(b){i.then(null,function(c){b(c.data,c.status,c.headers,a)});return i};return i}function n(b,c,d){function e(a,b,c){m&&(200<=a&&a<300?m.put(w,[a,b,Mb(c)]):m.remove(w));f(b,a,c);i.$apply()}function f(a,
92 | c,d){c=Math.max(c,0);(200<=c&&c<300?n.resolve:n.reject)({data:a,status:c,headers:Nb(d),config:b})}function h(){var a=Ua(l.pendingRequests,b);a!==-1&&l.pendingRequests.splice(a,1)}var n=j.defer(),k=n.promise,m,p,w=r(b.url,b.params);l.pendingRequests.push(b);k.then(h,h);b.cache&&b.method=="GET"&&(m=J(b.cache)?b.cache:o);if(m)if(p=m.get(w))if(p.then)return p.then(h,h),p;else K(p)?f(p[1],p[0],U(p[2])):f(p,200,{});else m.put(w,k);p||a(b.method,w,c,e,d,b.timeout,b.withCredentials);return k}function r(a,
93 | b){if(!b)return a;var c=[];dc(b,function(a,b){a==null||a==p||(J(a)&&(a=ba(a)),c.push(encodeURIComponent(b)+"="+encodeURIComponent(a)))});return a+(a.indexOf("?")==-1?"?":"&")+c.join("&")}var o=c("$http"),w=[];m(e,function(a){w.push(G(a)?k.get(a):k.invoke(a))});l.pendingRequests=[];(function(a){m(arguments,function(a){l[a]=function(b,c){return l(D(c||{},{method:a,url:b}))}})})("get","delete","head","jsonp");(function(a){m(arguments,function(a){l[a]=function(b,c,d){return l(D(d||{},{method:a,url:b,
94 | data:c}))}})})("post","put");l.defaults=d;return l}]}function Vc(){this.$get=["$browser","$window","$document",function(b,a,c){return Wc(b,Xc,b.defer,a.angular.callbacks,c[0],a.location.protocol.replace(":",""))}]}function Wc(b,a,c,d,e,g){function h(a,b){var c=e.createElement("script"),d=function(){e.body.removeChild(c);b&&b()};c.type="text/javascript";c.src=a;Z?c.onreadystatechange=function(){/loaded|complete/.test(c.readyState)&&d()}:c.onload=c.onerror=d;e.body.appendChild(c)}return function(e,
95 | i,j,k,l,n,r){function o(a,c,d,e){c=(i.match(Fb)||["",g])[1]=="file"?d?200:404:c;a(c==1223?204:c,d,e);b.$$completeOutstandingRequest(x)}b.$$incOutstandingRequestCount();i=i||b.url();if(C(e)=="jsonp"){var p="_"+(d.counter++).toString(36);d[p]=function(a){d[p].data=a};h(i.replace("JSON_CALLBACK","angular.callbacks."+p),function(){d[p].data?o(k,200,d[p].data):o(k,-2);delete d[p]})}else{var q=new a;q.open(e,i,!0);m(l,function(a,b){a&&q.setRequestHeader(b,a)});var t;q.onreadystatechange=function(){q.readyState==
96 | 4&&o(k,t||q.status,q.responseText,q.getAllResponseHeaders())};if(r)q.withCredentials=!0;q.send(j||"");n>0&&c(function(){t=-1;q.abort()},n)}}}function Yc(){this.$get=function(){return{id:"en-us",NUMBER_FORMATS:{DECIMAL_SEP:".",GROUP_SEP:",",PATTERNS:[{minInt:1,minFrac:0,maxFrac:3,posPre:"",posSuf:"",negPre:"-",negSuf:"",gSize:3,lgSize:3},{minInt:1,minFrac:2,maxFrac:2,posPre:"\u00a4",posSuf:"",negPre:"(\u00a4",negSuf:")",gSize:3,lgSize:3}],CURRENCY_SYM:"$"},DATETIME_FORMATS:{MONTH:"January,February,March,April,May,June,July,August,September,October,November,December".split(","),
97 | SHORTMONTH:"Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec".split(","),DAY:"Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday".split(","),SHORTDAY:"Sun,Mon,Tue,Wed,Thu,Fri,Sat".split(","),AMPMS:["AM","PM"],medium:"MMM d, y h:mm:ss a","short":"M/d/yy h:mm a",fullDate:"EEEE, MMMM d, y",longDate:"MMMM d, y",mediumDate:"MMM d, y",shortDate:"M/d/yy",mediumTime:"h:mm:ss a",shortTime:"h:mm a"},pluralCat:function(b){return b===1?"one":"other"}}}}function Zc(){this.$get=["$rootScope","$browser","$q",
98 | "$exceptionHandler",function(b,a,c,d){function e(e,f,i){var j=c.defer(),k=j.promise,l=s(i)&&!i,f=a.defer(function(){try{j.resolve(e())}catch(a){j.reject(a),d(a)}l||b.$apply()},f),i=function(){delete g[k.$$timeoutId]};k.$$timeoutId=f;g[f]=j;k.then(i,i);return k}var g={};e.cancel=function(b){return b&&b.$$timeoutId in g?(g[b.$$timeoutId].reject("canceled"),a.defer.cancel(b.$$timeoutId)):!1};return e}]}function Pb(b){function a(a,e){return b.factory(a+c,e)}var c="Filter";this.register=a;this.$get=["$injector",
99 | function(a){return function(b){return a.get(b+c)}}];a("currency",Qb);a("date",Rb);a("filter",$c);a("json",ad);a("limitTo",bd);a("lowercase",cd);a("number",Sb);a("orderBy",Tb);a("uppercase",dd)}function $c(){return function(b,a){if(!(b instanceof Array))return b;var c=[];c.check=function(a){for(var b=0;b
100 | -1;case "object":for(var c in a)if(c.charAt(0)!=="$"&&d(a[c],b))return!0;return!1;case "array":for(c=0;c=k+l)for(var j=h.length-k,n=0;n0||e>-c)e+=c;e===0&&c==-12&&(e=12);return hb(e,a,d)}}function La(b,a){return function(c,d){var e=c["get"+b](),g=la(a?"SHORT"+b:b);return d[g][e]}}function Rb(b){function a(a){var b;if(b=a.match(c)){var a=new Date(0),g=0,h=0;b[9]&&(g=H(b[9]+b[10]),h=H(b[9]+b[11]));a.setUTCFullYear(H(b[1]),H(b[2])-1,H(b[3]));a.setUTCHours(H(b[4]||0)-g,H(b[5]||0)-h,H(b[6]||0),H(b[7]||0))}return a}
104 | var c=/^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d{3}))?)?)?(Z|([+-])(\d\d):?(\d\d)))?$/;return function(c,e){var g="",h=[],f,i,e=e||"mediumDate",e=b.DATETIME_FORMATS[e]||e;G(c)&&(c=ed.test(c)?H(c):a(c));va(c)&&(c=new Date(c));if(!ma(c))return c;for(;e;)(i=fd.exec(e))?(h=h.concat(ga.call(i,1)),e=h.pop()):(h.push(e),e=null);m(h,function(a){f=gd[a];g+=f?f(c,b.DATETIME_FORMATS):a.replace(/(^'|'$)/g,"").replace(/''/g,"'")});return g}}function ad(){return function(b){return ba(b,!0)}}
105 | function bd(){return function(b,a){if(!(b instanceof Array))return b;var a=H(a),c=[],d,e;if(!b||!(b instanceof Array))return c;a>b.length?a=b.length:a<-b.length&&(a=-b.length);a>0?(d=0,e=a):(d=b.length+a,e=b.length);for(;dl?(d.$setValidity("maxlength",!1),p):(d.$setValidity("maxlength",!0),a)};d.$parsers.push(c);d.$formatters.push(c)}}function ib(b,a){b="ngClass"+b;return R(function(c,d,e){c.$watch(e[b],function(b,e){if(a===!0||c.$index%
111 | 2===a)e&&b!==e&&(J(e)&&!K(e)&&(e=Ta(e,function(a,b){if(a)return b})),d.removeClass(K(e)?e.join(" "):e)),J(b)&&!K(b)&&(b=Ta(b,function(a,b){if(a)return b})),b&&d.addClass(K(b)?b.join(" "):b)},!0)})}var C=function(b){return G(b)?b.toLowerCase():b},la=function(b){return G(b)?b.toUpperCase():b},z=T.Error,Z=H((/msie (\d+)/.exec(C(navigator.userAgent))||[])[1]),u,ha,ga=[].slice,Ra=[].push,Sa=Object.prototype.toString,Yb=T.angular||(T.angular={}),sa,Cb,Y=["0","0","0"];x.$inject=[];ya.$inject=[];Cb=Z<9?function(b){b=
112 | b.nodeName?b:b[0];return b.scopeName&&b.scopeName!="HTML"?la(b.scopeName+":"+b.nodeName):b.nodeName}:function(b){return b.nodeName?b.nodeName:b[0].nodeName};var jc=/[A-Z]/g,hd={full:"1.0.1",major:1,minor:0,dot:1,codeName:"thorium-shielding"},Ba=P.cache={},Aa=P.expando="ng-"+(new Date).getTime(),nc=1,id=T.document.addEventListener?function(b,a,c){b.addEventListener(a,c,!1)}:function(b,a,c){b.attachEvent("on"+a,c)},tb=T.document.removeEventListener?function(b,a,c){b.removeEventListener(a,c,!1)}:function(b,
113 | a,c){b.detachEvent("on"+a,c)},lc=/([\:\-\_]+(.))/g,mc=/^moz([A-Z])/,ta=P.prototype={ready:function(b){function a(){c||(c=!0,b())}var c=!1;this.bind("DOMContentLoaded",a);P(T).bind("load",a)},toString:function(){var b=[];m(this,function(a){b.push(""+a)});return"["+b.join(", ")+"]"},eq:function(b){return b>=0?u(this[b]):u(this[this.length+b])},length:0,push:Ra,sort:[].sort,splice:[].splice},Ea={};m("multiple,selected,checked,disabled,readOnly,required".split(","),function(b){Ea[C(b)]=b});var zb={};
114 | m("input,select,option,textarea,button,form".split(","),function(b){zb[la(b)]=!0});m({data:ub,inheritedData:Da,scope:function(b){return Da(b,"$scope")},controller:xb,injector:function(b){return Da(b,"$injector")},removeAttr:function(b,a){b.removeAttribute(a)},hasClass:Ca,css:function(b,a,c){a=qb(a);if(s(c))b.style[a]=c;else{var d;Z<=8&&(d=b.currentStyle&&b.currentStyle[a],d===""&&(d="auto"));d=d||b.style[a];Z<=8&&(d=d===""?p:d);return d}},attr:function(b,a,c){var d=C(a);if(Ea[d])if(s(c))c?(b[a]=!0,
115 | b.setAttribute(a,d)):(b[a]=!1,b.removeAttribute(d));else return b[a]||(b.attributes.getNamedItem(a)||x).specified?d:p;else if(s(c))b.setAttribute(a,c);else if(b.getAttribute)return b=b.getAttribute(a,2),b===null?p:b},prop:function(b,a,c){if(s(c))b[a]=c;else return b[a]},text:D(Z<9?function(b,a){if(b.nodeType==1){if(v(a))return b.innerText;b.innerText=a}else{if(v(a))return b.nodeValue;b.nodeValue=a}}:function(b,a){if(v(a))return b.textContent;b.textContent=a},{$dv:""}),val:function(b,a){if(v(a))return b.value;
116 | b.value=a},html:function(b,a){if(v(a))return b.innerHTML;for(var c=0,d=b.childNodes;c":function(a,c,d,e){return d(a,c)>e(a,c)},"<=":function(a,c,d,e){return d(a,c)<=e(a,c)},">=":function(a,c,d,e){return d(a,c)>=e(a,c)},
124 | "&&":function(a,c,d,e){return d(a,c)&&e(a,c)},"||":function(a,c,d,e){return d(a,c)||e(a,c)},"&":function(a,c,d,e){return d(a,c)&e(a,c)},"|":function(a,c,d,e){return e(a,c)(a,c,d(a,c))},"!":function(a,c,d){return!d(a,c)}},Kc={n:"\n",f:"\u000c",r:"\r",t:"\t",v:"\u000b","'":"'",'"':'"'},gb={},Xc=T.XMLHttpRequest||function(){try{return new ActiveXObject("Msxml2.XMLHTTP.6.0")}catch(a){}try{return new ActiveXObject("Msxml2.XMLHTTP.3.0")}catch(c){}try{return new ActiveXObject("Msxml2.XMLHTTP")}catch(d){}throw new z("This browser does not support XMLHttpRequest.");
125 | };Pb.$inject=["$provide"];Qb.$inject=["$locale"];Sb.$inject=["$locale"];var Vb=".",gd={yyyy:O("FullYear",4),yy:O("FullYear",2,0,!0),y:O("FullYear",1),MMMM:La("Month"),MMM:La("Month",!0),MM:O("Month",2,1),M:O("Month",1,1),dd:O("Date",2),d:O("Date",1),HH:O("Hours",2),H:O("Hours",1),hh:O("Hours",2,-12),h:O("Hours",1,-12),mm:O("Minutes",2),m:O("Minutes",1),ss:O("Seconds",2),s:O("Seconds",1),EEEE:La("Day"),EEE:La("Day",!0),a:function(a,c){return a.getHours()<12?c.AMPMS[0]:c.AMPMS[1]},Z:function(a){a=a.getTimezoneOffset();
126 | return hb(a/60,2)+hb(Math.abs(a%60),2)}},fd=/((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z))(.*)/,ed=/^\d+$/;Rb.$inject=["$locale"];var cd=B(C),dd=B(la);Tb.$inject=["$parse"];var jd=B({restrict:"E",compile:function(a,c){c.href||c.$set("href","");return function(a,c){c.bind("click",function(a){c.attr("href")||a.preventDefault()})}}}),jb={};m(Ea,function(a,c){var d=ea("ng-"+c);jb[d]=function(){return{priority:100,compile:function(){return function(a,g,h){a.$watch(h[d],function(a){h.$set(c,
127 | !!a)})}}}}});m(["src","href"],function(a){var c=ea("ng-"+a);jb[c]=function(){return{priority:99,link:function(d,e,g){g.$observe(c,function(c){g.$set(a,c);Z&&e.prop(a,c)})}}}});var Oa={$addControl:x,$removeControl:x,$setValidity:x,$setDirty:x};Wb.$inject=["$element","$attrs","$scope"];var Ra={name:"form",restrict:"E",controller:Wb,compile:function(){return{pre:function(a,c,d,e){d.action||c.bind("submit",function(a){a.preventDefault()});var g=c.parent().controller("form"),h=d.name||d.ngForm;h&&(a[h]=
128 | e);g&&c.bind("$destroy",function(){g.$removeControl(e);h&&(a[h]=p);D(e,Oa)})}}}},kd=B(Ra),ld=B(D(U(Ra),{restrict:"EAC"})),md=/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/,nd=/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/,od=/^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/,$b={text:Qa,number:function(a,c,d,e,g,h){Qa(a,c,d,e,g,h);e.$parsers.push(function(a){var c=S(a);return c||od.test(a)?(e.$setValidity("number",!0),a===""?null:c?a:parseFloat(a)):(e.$setValidity("number",
129 | !1),p)});e.$formatters.push(function(a){return S(a)?"":""+a});if(d.min){var f=parseFloat(d.min),a=function(a){return!S(a)&&ai?(e.$setValidity("max",!1),p):(e.$setValidity("max",!0),a)};e.$parsers.push(d);e.$formatters.push(d)}e.$formatters.push(function(a){return S(a)||va(a)?(e.$setValidity("number",!0),a):(e.$setValidity("number",!1),
130 | p)})},url:function(a,c,d,e,g,h){Qa(a,c,d,e,g,h);a=function(a){return S(a)||md.test(a)?(e.$setValidity("url",!0),a):(e.$setValidity("url",!1),p)};e.$formatters.push(a);e.$parsers.push(a)},email:function(a,c,d,e,g,h){Qa(a,c,d,e,g,h);a=function(a){return S(a)||nd.test(a)?(e.$setValidity("email",!0),a):(e.$setValidity("email",!1),p)};e.$formatters.push(a);e.$parsers.push(a)},radio:function(a,c,d,e){v(d.name)&&c.attr("name",wa());c.bind("click",function(){c[0].checked&&a.$apply(function(){e.$setViewValue(d.value)})});
131 | e.$render=function(){c[0].checked=d.value==e.$viewValue};d.$observe("value",e.$render)},checkbox:function(a,c,d,e){var g=d.ngTrueValue,h=d.ngFalseValue;G(g)||(g=!0);G(h)||(h=!1);c.bind("click",function(){a.$apply(function(){e.$setViewValue(c[0].checked)})});e.$render=function(){c[0].checked=e.$viewValue};e.$formatters.push(function(a){return a===g});e.$parsers.push(function(a){return a?g:h})},hidden:x,button:x,submit:x,reset:x},ac=["$browser","$sniffer",function(a,c){return{restrict:"E",require:"?ngModel",
132 | link:function(d,e,g,h){h&&($b[C(g.type)]||$b.text)(d,e,g,h,c,a)}}}],Na="ng-valid",Ma="ng-invalid",Pa="ng-pristine",Xb="ng-dirty",pd=["$scope","$exceptionHandler","$attrs","$element","$parse",function(a,c,d,e,g){function h(a,c){c=c?"-"+$a(c,"-"):"";e.removeClass((a?Ma:Na)+c).addClass((a?Na:Ma)+c)}this.$modelValue=this.$viewValue=Number.NaN;this.$parsers=[];this.$formatters=[];this.$viewChangeListeners=[];this.$pristine=!0;this.$dirty=!1;this.$valid=!0;this.$invalid=!1;this.$name=d.name;var g=g(d.ngModel),
133 | f=g.assign;if(!f)throw z(Db+d.ngModel+" ("+oa(e)+")");this.$render=x;var i=e.inheritedData("$formController")||Oa,j=0,k=this.$error={};e.addClass(Pa);h(!0);this.$setValidity=function(a,c){if(k[a]!==!c){if(c){if(k[a]&&j--,!j)h(!0),this.$valid=!0,this.$invalid=!1}else h(!1),this.$invalid=!0,this.$valid=!1,j++;k[a]=!c;h(c,a);i.$setValidity(a,c,this)}};this.$setViewValue=function(d){this.$viewValue=d;if(this.$pristine)this.$dirty=!0,this.$pristine=!1,e.removeClass(Pa).addClass(Xb),i.$setDirty();m(this.$parsers,
134 | function(a){d=a(d)});if(this.$modelValue!==d)this.$modelValue=d,f(a,d),m(this.$viewChangeListeners,function(a){try{a()}catch(d){c(d)}})};var l=this;a.$watch(g,function(a){if(l.$modelValue!==a){var c=l.$formatters,d=c.length;for(l.$modelValue=a;d--;)a=c[d](a);if(l.$viewValue!==a)l.$viewValue=a,l.$render()}})}],qd=function(){return{require:["ngModel","^?form"],controller:pd,link:function(a,c,d,e){var g=e[0],h=e[1]||Oa;h.$addControl(g);c.bind("$destroy",function(){h.$removeControl(g)})}}},rd=B({require:"ngModel",
135 | link:function(a,c,d,e){e.$viewChangeListeners.push(function(){a.$eval(d.ngChange)})}}),bc=function(){return{require:"?ngModel",link:function(a,c,d,e){if(e){d.required=!0;var g=function(a){if(d.required&&(S(a)||a===!1))e.$setValidity("required",!1);else return e.$setValidity("required",!0),a};e.$formatters.push(g);e.$parsers.unshift(g);d.$observe("required",function(){g(e.$viewValue)})}}}},sd=function(){return{require:"ngModel",link:function(a,c,d,e){var g=(a=/\/(.*)\//.exec(d.ngList))&&RegExp(a[1])||
136 | d.ngList||",",h=function(a){var c=[];a&&m(a.split(g),function(a){a&&c.push(Q(a))});return c};e.$parsers.push(h);e.$formatters.push(function(a){return K(a)&&!fa(h(e.$viewValue),a)?a.join(", "):p})}}},td=/^(true|false|\d+)$/,ud=function(){return{priority:100,compile:function(a,c){return td.test(c.ngValue)?function(a,c,g){g.$set("value",a.$eval(g.ngValue))}:function(a,c,g){a.$watch(g.ngValue,function(a){g.$set("value",a,!1)})}}}},vd=R(function(a,c,d){c.addClass("ng-binding").data("$binding",d.ngBind);
137 | a.$watch(d.ngBind,function(a){c.text(a==p?"":a)})}),wd=["$interpolate",function(a){return function(c,d,e){c=a(d.attr(e.$attr.ngBindTemplate));d.addClass("ng-binding").data("$binding",c);e.$observe("ngBindTemplate",function(a){d.text(a)})}}],xd=[function(){return function(a,c,d){c.addClass("ng-binding").data("$binding",d.ngBindHtmlUnsafe);a.$watch(d.ngBindHtmlUnsafe,function(a){c.html(a||"")})}}],yd=ib("",!0),zd=ib("Odd",0),Ad=ib("Even",1),Bd=R({compile:function(a,c){c.$set("ngCloak",p);a.removeClass("ng-cloak")}}),
138 | Cd=[function(){return{scope:!0,controller:"@"}}],Dd=["$sniffer",function(a){return{priority:1E3,compile:function(){a.csp=!0}}}],cc={};m("click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave".split(" "),function(a){var c=ea("ng-"+a);cc[c]=["$parse",function(d){return function(e,g,h){var f=d(h[c]);g.bind(C(a),function(a){e.$apply(function(){f(e,{$event:a})})})}}]});var Ed=R(function(a,c,d){c.bind("submit",function(){a.$apply(d.ngSubmit)})}),Fd=["$http","$templateCache",
139 | "$anchorScroll","$compile",function(a,c,d,e){return{restrict:"ECA",terminal:!0,compile:function(g,h){var f=h.ngInclude||h.src,i=h.onload||"",j=h.autoscroll;return function(g,h){var n=0,m,o=function(){m&&(m.$destroy(),m=null);h.html("")};g.$watch(f,function(f){var q=++n;f?a.get(f,{cache:c}).success(function(a){q===n&&(m&&m.$destroy(),m=g.$new(),h.html(a),e(h.contents())(m),s(j)&&(!j||g.$eval(j))&&d(),m.$emit("$includeContentLoaded"),g.$eval(i))}).error(function(){q===n&&o()}):o()})}}}}],Gd=R({compile:function(){return{pre:function(a,
140 | c,d){a.$eval(d.ngInit)}}}}),Hd=R({terminal:!0,priority:1E3}),Id=["$locale","$interpolate",function(a,c){var d=/{}/g;return{restrict:"EA",link:function(e,g,h){var f=h.count,i=g.attr(h.$attr.when),j=h.offset||0,k=e.$eval(i),l={};m(k,function(a,e){l[e]=c(a.replace(d,"{{"+f+"-"+j+"}}"))});e.$watch(function(){var c=parseFloat(e.$eval(f));return isNaN(c)?"":(k[c]||(c=a.pluralCat(c-j)),l[c](e,g,!0))},function(a){g.text(a)})}}}],Jd=R({transclude:"element",priority:1E3,terminal:!0,compile:function(a,c,d){return function(a,
141 | c,h){var f=h.ngRepeat,h=f.match(/^\s*(.+)\s+in\s+(.*)\s*$/),i,j,k;if(!h)throw z("Expected ngRepeat in form of '_item_ in _collection_' but got '"+f+"'.");f=h[1];i=h[2];h=f.match(/^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$/);if(!h)throw z("'item' in 'item in collection' should be identifier or (key, value) but got '"+f+"'.");j=h[3]||h[1];k=h[2];var l=new db;a.$watch(function(a){var e,f,h=a.$eval(i),m=fc(h,!0),p,u=new db,A,y,v,s,z=c;if(K(h))v=h||[];else{v=[];for(A in h)h.hasOwnProperty(A)&&A.charAt(0)!=
142 | "$"&&v.push(A);v.sort()}e=0;for(f=v.length;ex;)u.pop().element.remove()}for(;v.length>w;)v.pop()[0].element.remove()}var h;if(!(h=w.match(d)))throw z("Expected ngOptions in form of '_select_ (as _label_)? for (_key_,)?_value_ in _collection_' but got '"+w+"'.");var j=c(h[2]||h[1]),k=h[4]||h[6],l=h[5],m=c(h[3]||""),n=c(h[2]?h[1]:k),r=c(h[7]),v=[[{element:f,label:""}]];q&&(a(q)(e),q.removeClass("ng-scope"),q.remove());f.html("");f.bind("change",function(){e.$apply(function(){var a,
152 | c=r(e)||[],d={},h,i,j,m,q,s;if(o){i=[];m=0;for(s=v.length;m@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak{display:none;}ng\\:form{display:block;}');
158 |
--------------------------------------------------------------------------------
/app/js/lib/angular/version.txt:
--------------------------------------------------------------------------------
1 | 1.0.1
--------------------------------------------------------------------------------
/app/js/puzzle/slidingPuzzle.js:
--------------------------------------------------------------------------------
1 | (function(angular) {
2 | 'use strict';
3 |
4 | var module = angular.module('slidingPuzzle', []);
5 |
6 | /**
7 | * Service
8 | */
9 | module.factory('slidingPuzzle', function() {
10 | function shuffle(a) {
11 | var q;
12 | for (var j, x, i = a.length; i; j = parseInt(Math.random() * i, 10), x = a[--i], a[i] = a[j], a[j] = x) { q = 0; }
13 | return a;
14 | }
15 |
16 | function SlidingPuzzle(rows, cols) {
17 | /**
18 | * Puzzle grid
19 | * @type {Array}
20 | */
21 | this.grid = [];
22 |
23 | /**
24 | * Moves count
25 | * @type {Number}
26 | */
27 | this.moves = 0;
28 |
29 | /**
30 | * Moves tile
31 | * @param srow
32 | * @param scol
33 | */
34 | this.move = function(srow, scol) {
35 | var dirs = [[1, 0], [-1, 0], [0, 1], [0, -1]],
36 | tref, trow, tcol;
37 |
38 | for (var d = 0; d < dirs.length; d++) {
39 | trow = srow + dirs[d][0];
40 | tcol = scol + dirs[d][1];
41 | if (this.grid[trow] && this.grid[trow][tcol] && this.grid[trow][tcol].empty) {
42 | tref = this.grid[srow][scol];
43 | this.grid[srow][scol] = this.grid[trow][tcol];
44 | this.grid[trow][tcol] = tref;
45 | this.moves++;
46 | }
47 | }
48 | };
49 |
50 | /**
51 | * Shuffles grid
52 | */
53 | this.shuffle = function() {
54 | var tiles = [];
55 | this.traverse(function(tile) {
56 | tiles.push(tile);
57 | });
58 | shuffle(tiles);
59 | this.traverse(function(tile, row, col) {
60 | this.grid[row][col] = tiles.shift();
61 | });
62 | this.moves = 0;
63 | };
64 |
65 | /**
66 | * Solves puzzle
67 | */
68 | this.solve = function() {
69 | var tiles = [];
70 | this.traverse(function(tile) {
71 | tiles.push(tile);
72 | });
73 | tiles.sort(function(x, y) {
74 | return (x.id - y.id);
75 | });
76 | this.traverse(function(tile, row, col) {
77 | this.grid[row][col] = tiles.shift();
78 | });
79 | };
80 |
81 | /**
82 | * Is solved?
83 | * @type {Boolean}
84 | */
85 | this.isSolved = function() {
86 | var id = 1;
87 | for (var row = 0; row < rows; row++) {
88 | for (var col = 0; col < cols; col++) {
89 | if (this.grid[row][col].id !== id++) {
90 | return false;
91 | }
92 | }
93 | }
94 | return true;
95 | };
96 |
97 | /**
98 | * Traverses grid and executes fn on every tile
99 | * @param fn
100 | */
101 | this.traverse = function(fn) {
102 | for (var row = 0; row < rows; row++) {
103 | for (var col = 0; col < cols; col++) {
104 | fn.call(this, this.grid && this.grid[row] ? this.grid[row][col] : undefined, row, col);
105 | }
106 | }
107 | };
108 |
109 | // initialize grid
110 | var id = 1;
111 | this.traverse(function(tile, row, col) {
112 | if (!this.grid[row]) {
113 | this.grid[row] = [];
114 | }
115 | this.grid[row][col] = {
116 | id: id++,
117 | empty: (row === rows - 1) && (col === cols - 1)
118 | };
119 | if (this.grid[row][col].empty) {
120 | this.empty = this.grid[row][col];
121 | }
122 | });
123 | }
124 |
125 | return function(rows, cols) {
126 | return new SlidingPuzzle(rows, cols);
127 | };
128 | });
129 |
130 | /**
131 | * Directive
132 | */
133 | module.directive('slidingPuzzle', function(slidingPuzzle) {
134 | return {
135 | restrict: 'EA',
136 | replace: true,
137 | template: '' +
138 | '' +
139 | ' | ' +
140 | '
' +
141 | '
',
142 | scope: {
143 | size: '@',
144 | src: '@',
145 | api: '='
146 | },
147 | link: function(scope, element, attrs) {
148 | var rows, cols,
149 | loading = true,
150 | image = new Image();
151 |
152 | function create() {
153 | scope.puzzle = slidingPuzzle(rows, cols);
154 |
155 | if (attrs.api) {
156 | scope.api = scope.puzzle;
157 | }
158 |
159 | tile();
160 | }
161 |
162 | function tile() {
163 | if (loading) {
164 | return;
165 | }
166 |
167 | var width = image.width / cols,
168 | height = image.height / rows;
169 |
170 | scope.puzzle.traverse(function(tile, row, col) {
171 | tile.style = {
172 | width: width + 'px',
173 | height: height + 'px',
174 | background: (tile.empty ? 'none' : "url('" + scope.src + "') no-repeat -" + (col * width) + 'px -' + (row * height) + 'px')
175 | };
176 | });
177 |
178 | scope.puzzle.shuffle();
179 | }
180 |
181 | attrs.$observe('size', function(size) {
182 | size = size.split('x');
183 | if (size[0] >= 2 && size[1] >= 2) {
184 | rows = size[0];
185 | cols = size[1];
186 | create();
187 | }
188 | });
189 |
190 | attrs.$observe('src', function(src) {
191 | loading = true;
192 | image.src = src;
193 | image.onload = function() {
194 | loading = false;
195 | scope.$apply(function() {
196 | tile();
197 | });
198 | };
199 | });
200 | }
201 | };
202 | });
203 | })(window.angular);
204 |
--------------------------------------------------------------------------------
/app/js/puzzle/wordSearchPuzzle.js:
--------------------------------------------------------------------------------
1 | (function(angular) {
2 | 'use strict';
3 |
4 | var module = angular.module('wordSearchPuzzle', []);
5 |
6 | /**
7 | * Service
8 | */
9 | module.factory('wordSearchPuzzle', function() {
10 | /**
11 | * Word search puzzle
12 | * @param matrix
13 | * @param words
14 | * @constructor
15 | */
16 | function WordSearchPuzzle(matrix, words) {
17 | var maxRow = 0,
18 | maxCol = 0;
19 |
20 | /**
21 | * Matrix
22 | * @type {Array}
23 | */
24 | this.matrix = [];
25 |
26 | /**
27 | * Words
28 | * @type {Array}
29 | */
30 | this.words = [];
31 |
32 | /**
33 | * Is solved?
34 | * @type {Boolean}
35 | */
36 | this.solved = false;
37 |
38 | // parse matrix and words
39 | angular.forEach(matrix, function(letters, row) {
40 | angular.forEach(letters, function(letter, col) {
41 | var item = {
42 | letter: letter,
43 | col: col,
44 | row: row,
45 | used: false
46 | };
47 | if (!this.matrix[row]) {
48 | this.matrix[row] = [];
49 | }
50 | this.matrix[row].push(item);
51 | maxCol = col;
52 | }, this);
53 | maxRow = row;
54 | }, this);
55 | angular.forEach(words, function(word) {
56 | this.words.push({
57 | name: word
58 | });
59 | }, this);
60 |
61 | /**
62 | * Returns matrix item by coords
63 | * @param col
64 | * @param row
65 | * @return {*}
66 | */
67 | this.getItem = function(col, row) {
68 | return (this.matrix[row] ? this.matrix[row][col] : undefined);
69 | };
70 |
71 | /**
72 | * Returns matrix items by start/end coords
73 | * @param colFrom
74 | * @param rowFrom
75 | * @param colTo
76 | * @param rowTo
77 | * @return {Array}
78 | */
79 | this.getItems = function(colFrom, rowFrom, colTo, rowTo) {
80 | var items = [];
81 |
82 | if (rowTo > maxRow) {
83 | rowTo = maxRow;
84 | }
85 | if (colTo > maxCol) {
86 | colTo = maxCol;
87 | }
88 |
89 | if (this.getItem(colTo, rowTo) === undefined) {
90 | return items;
91 | }
92 |
93 | if (colFrom === colTo || rowFrom === rowTo || Math.abs(colTo - colFrom) === Math.abs(rowTo - rowFrom)) {
94 | var shiftX = (colFrom === colTo ? 0 : (colTo > colFrom ? 1 : -1)),
95 | shiftY = (rowFrom === rowTo ? 0 : (rowTo > rowFrom ? 1 : -1)),
96 | col = colFrom,
97 | row = rowFrom;
98 |
99 | items.push(this.getItem(col, row));
100 | do {
101 | col += shiftX;
102 | row += shiftY;
103 | items.push(this.getItem(col, row));
104 | } while (col !== colTo || row !== rowTo);
105 | }
106 |
107 | return items;
108 | };
109 |
110 | /**
111 | * Check items - find word
112 | * @param items
113 | */
114 | this.lookup = function(items) {
115 | if (!items.length) {
116 | return;
117 | }
118 |
119 | // create words
120 | var words = [''];
121 | angular.forEach(items, function(item) {
122 | words[0] += item.letter;
123 | });
124 | words.push(words[0].split('').reverse().join('')); // word in reverse order (when selecting)
125 |
126 | // check words
127 | this.solved = true;
128 | angular.forEach(this.words, function(word) {
129 | if (word.found) {
130 | return;
131 | }
132 | angular.forEach(words, function(itemWord) {
133 | if (word.name === itemWord) {
134 | word.found = true;
135 | angular.forEach(items, function(item) {
136 | item.found = true;
137 | });
138 | }
139 | });
140 | if (!word.found) {
141 | this.solved = false;
142 | }
143 | }, this);
144 | };
145 |
146 | /**
147 | * Solves puzzle
148 | */
149 | this.solve = function() {
150 | var start = {},
151 | directions = {
152 | N: [0, -1], E: [1, 0], S: [0, 1], W: [-1, 0],
153 | NE: [1, -1], NW: [-1, -1], SE: [1, 1], SW: [-1, 1]
154 | };
155 |
156 | // group items by letters for faster search
157 | angular.forEach(this.matrix, function(items) {
158 | angular.forEach(items, function(item) {
159 | if (!start[item.letter]) {
160 | start[item.letter] = [];
161 | }
162 | start[item.letter].push(item);
163 | });
164 | });
165 |
166 | angular.forEach(this.words, function(word) {
167 | angular.forEach(start[word.name.charAt(0)], function(start) {
168 | if (word.found) {
169 | return;
170 | }
171 | angular.forEach(directions, function(shift) {
172 | if (word.found) {
173 | return;
174 | }
175 | this.lookup(this.getItems(
176 | start.col, start.row,
177 | start.col + (word.name.length - 1) * shift[0],
178 | start.row + (word.name.length - 1) * shift[1]
179 | ));
180 | }, this);
181 | }, this);
182 | }, this);
183 | };
184 | }
185 |
186 | return function(matrix, words) {
187 | return new WordSearchPuzzle(matrix, words);
188 | };
189 | });
190 |
191 | /**
192 | * Directive
193 | */
194 | module.directive('wordSearchPuzzle', function(wordSearchPuzzle) {
195 | return {
196 | restrict: 'EA',
197 | replace: true,
198 | template: '' +
199 | '' +
200 | '' +
203 | ' {{item.letter}}' +
204 | ' | ' +
205 | '
' +
206 | '
',
207 | scope: {
208 | matrix: '=',
209 | words: '=',
210 | api: '='
211 | },
212 | link: function(scope, element, attrs) {
213 | var selectFrom;
214 |
215 | // setup puzzle
216 | scope.$watch('matrix', function(matrix) {
217 | scope.puzzle = wordSearchPuzzle(matrix, scope.words);
218 |
219 | // link service
220 | if (attrs.api) {
221 | scope.api = scope.puzzle;
222 | }
223 | });
224 |
225 | /**
226 | * Selected items
227 | * @type {Array}
228 | */
229 | scope.selected = [];
230 |
231 | /**
232 | * Selection start
233 | * @param item
234 | */
235 | scope.selectStart = function(item) {
236 | selectFrom = item;
237 | };
238 |
239 | /**
240 | * Selection enter (over)
241 | * @param item
242 | */
243 | scope.selectEnter = function(item) {
244 | if (selectFrom) {
245 | scope.selected = scope.puzzle.getItems(selectFrom.col, selectFrom.row, item.col, item.row);
246 | }
247 | };
248 |
249 | /**
250 | * Selection end
251 | */
252 | scope.selectEnd = function() {
253 | selectFrom = null;
254 | scope.puzzle.lookup(scope.selected);
255 | scope.selected = [];
256 | };
257 |
258 | // propagate selection state to matrix
259 | scope.$watch('selected', function(newItems, oldItems) {
260 | angular.forEach(oldItems, function(item) {
261 | item.selected = false;
262 | });
263 | angular.forEach(newItems, function(item) {
264 | item.selected = true;
265 | });
266 | });
267 | }
268 | };
269 | });
270 | })(window.angular);
271 |
--------------------------------------------------------------------------------
/test/config.js:
--------------------------------------------------------------------------------
1 | basePath = '../';
2 |
3 | port = 8088;
4 |
5 | files = [
6 | JASMINE,
7 | JASMINE_ADAPTER,
8 | 'app/js/lib/angular/angular.js',
9 | 'app/js/lib/angular/angular-mocks.js',
10 | 'app/js/puzzle/*.js',
11 | 'app/js/*.js',
12 | 'test/unit/*.js'
13 | ];
14 |
15 | // list of files to exclude
16 | exclude = [];
17 |
18 | // use dots reporter, as travis terminal does not support escaping sequences
19 | // possible values: 'dots' || 'progress'
20 | reporter = 'dots';
21 |
22 | // cli runner port
23 | runnerPort = 9100;
24 |
25 | // enable / disable colors in the output (reporters and logs)
26 | colors = true;
27 |
28 | // level of logging
29 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
30 | logLevel = LOG_INFO | LOG_WARN | LOG_ERROR;
31 |
32 | // enable / disable watching file and executing tests whenever any file changes
33 | autoWatch = true;
34 |
35 | // polling interval in ms (ignored on OS that support inotify)
36 | autoWatchInterval = 1;
37 |
38 | // Start these browsers, currently available:
39 | // - Chrome
40 | // - ChromeCanary
41 | // - Firefox
42 | // - Opera
43 | // - Safari
44 | // - PhantomJS
45 | browsers = ['PhantomJS'];
46 |
--------------------------------------------------------------------------------
/test/unit/appSpec.js:
--------------------------------------------------------------------------------
1 | describe('App', function() {
2 |
3 | beforeEach(module('puzzleApp'));
4 |
5 | describe('advancedCtrl', function() {
6 | var ctrl, scope;
7 |
8 | beforeEach(inject(function($controller, $rootScope) {
9 | scope = $rootScope.$new();
10 | ctrl = $controller('slidingAdvancedCtrl', {
11 | $scope: scope
12 | });
13 | }));
14 |
15 | it('should publish the puzzles model', function() {
16 | expect(scope.puzzles).toBeDefined();
17 | });
18 |
19 | it('should have correct size set in model', function() {
20 | scope.puzzles.forEach(function(item) {
21 | expect(item.rows).toBeGreaterThan(0);
22 | expect(item.cols).toBeGreaterThan(0);
23 | });
24 | });
25 | });
26 |
27 | });
28 |
--------------------------------------------------------------------------------
/test/unit/slidingPuzzleSpec.js:
--------------------------------------------------------------------------------
1 | describe('Sliding puzzle', function() {
2 | var puzzle,
3 | rows = 2,
4 | cols = 3;
5 |
6 | function fireEvent(element, event) {
7 | var evt;
8 | if (document.createEvent) {
9 | evt = document.createEvent('HTMLEvents');
10 | evt.initEvent(event, true, true);
11 | return !element.dispatchEvent(evt);
12 | } else {
13 | evt = document.createEventObject();
14 | return element.fireEvent('on' + event, evt);
15 | }
16 | }
17 |
18 | function click(element) {
19 | fireEvent(element, 'click');
20 | }
21 |
22 | beforeEach(module('slidingPuzzle'));
23 |
24 | /**
25 | * Controller tests
26 | */
27 | describe('puzzle object', function() {
28 | beforeEach(inject(function(slidingPuzzle) {
29 | puzzle = slidingPuzzle(rows, cols);
30 | }));
31 |
32 | function tile(row, col) {
33 | return puzzle.grid[row][col];
34 | }
35 |
36 | it('should initialize puzzle grid by size', function() {
37 | expect(puzzle.grid).toBeDefined();
38 | expect(puzzle.grid.length).toBe(rows);
39 | expect(puzzle.grid[0].length).toBe(cols);
40 | });
41 |
42 | it('should have empty flag on last item', function() {
43 | expect(tile(rows - 1, cols - 1).empty).toBe(true);
44 | expect(tile(0, 0).empty).toBe(false);
45 | });
46 |
47 | it('should move movable item', function() {
48 | var x = rows - 1,
49 | y = cols - 2,
50 | item = tile(x, y);
51 |
52 | puzzle.move(x, y);
53 | expect(tile(x, y)).not.toBe(item);
54 | expect(tile(x, y + 1)).toBe(item);
55 | expect(tile(x, y).empty).toBe(true);
56 | });
57 |
58 | it('should not move non-movable first item', function() {
59 | var x = 0,
60 | y = 0,
61 | item = tile(x, y);
62 |
63 | puzzle.move(x, y);
64 | expect(tile(x, y)).toBe(item);
65 | });
66 |
67 | it('should return correct "solved" status', function() {
68 | var x = rows - 1,
69 | y = cols - 2,
70 | item = tile(x, y);
71 |
72 | expect(puzzle.isSolved()).toBe(true);
73 | puzzle.move(x, y);
74 | expect(puzzle.isSolved()).toBe(false);
75 | puzzle.move(x, y + 1);
76 | expect(puzzle.isSolved()).toBe(true);
77 | });
78 |
79 | it('should solve puzzle when "solve" called', function() {
80 | var x = rows - 1,
81 | y = cols - 2,
82 | item = tile(x, y);
83 |
84 | expect(puzzle.isSolved()).toBe(true);
85 | puzzle.move(x, y);
86 | expect(puzzle.isSolved()).toBe(false);
87 | puzzle.solve();
88 | expect(puzzle.isSolved()).toBe(true);
89 | });
90 |
91 | });
92 |
93 | /**
94 | * Directive tests
95 | * @todo image mock, background positioning test
96 | */
97 | describe('puzzle directive', function() {
98 | var scope, template, templateScope,
99 | size = rows + 'x' + cols;
100 |
101 | function tile(row, col) {
102 | return template.find('tr').eq(row).find('td').eq(col);
103 | }
104 |
105 | beforeEach(function() {
106 | inject(function($compile, $rootScope) {
107 | scope = $rootScope.$new();
108 | scope.size = size;
109 | template = $compile('')(scope);
110 | templateScope = template.scope();
111 | scope.$apply();
112 | });
113 | });
114 |
115 | it('should initialize puzzle object in directive scope (only)', function() {
116 | expect(templateScope.puzzle).toBeDefined();
117 | expect(scope.puzzle).toBeUndefined();
118 | });
119 |
120 | it('should assign the puzzle object into parent scope by "api" accessor', function() {
121 | expect(typeof(scope.api)).toBe('object');
122 | expect(scope.api.grid).toBeDefined();
123 | expect(scope.api.grid.length).toBe(2);
124 | expect(scope.api.grid[0].length).toBe(3);
125 | });
126 |
127 | it('should initialize size in directive scope', function() {
128 | expect(templateScope.size).toBe(size);
129 | });
130 |
131 | it('should create 2x3 table', function() {
132 | expect(template.find('tr').length).toBe(2);
133 | expect(template.find('td').length).toBe(6);
134 | });
135 |
136 | it('should have puzzle-solved class on init', function() {
137 | expect(template.hasClass('puzzle-solved')).toBe(true);
138 | });
139 |
140 | it('should have puzzle-empty class on last element', function() {
141 | expect(tile(rows - 1, cols - 1).hasClass('puzzle-empty')).toBe(true);
142 | });
143 |
144 | it('should regenerate puzzle when size changed', function() {
145 | scope.size = '4x4';
146 | scope.$apply();
147 | expect(template.find('tr').length).toBe(4);
148 | expect(template.find('td').length).toBe(16);
149 | });
150 |
151 | it('should "move" movable tile on click', function() {
152 | var x = rows - 1,
153 | y = cols - 2,
154 | node = tile(x, y)[0];
155 |
156 | expect(node.title).toBe('5');
157 | click(node);
158 | expect(tile(x, y)[0]).not.toBe('5');
159 | expect(tile(x, y + 1)[0].title).toBe('5');
160 | expect(tile(x, y).hasClass('puzzle-empty')).toBe(true);
161 | });
162 |
163 | it('should not "move" non-movable first tile on click', function() {
164 | var node = tile(0, 0)[0];
165 | expect(node.title).toBe('1');
166 | click(node);
167 | expect(tile(0, 0)[0].title).toBe('1');
168 | });
169 |
170 | });
171 |
172 | });
--------------------------------------------------------------------------------