From a7506a34544f4df3ba65a854c81fadcca2eb303f Mon Sep 17 00:00:00 2001 From: Einar Egilsson Date: Wed, 2 Sep 2015 12:14:24 +0000 Subject: CRUD stuff almost ready --- chrome.manifest | 9 - chrome/browserOverlay.xul | 23 -- chrome/css/redirector.css | 173 ------------ chrome/help.html | 182 ------------- chrome/images/redirector.png | Bin 1462 -> 0 bytes chrome/images/statusactive.png | Bin 340 -> 0 bytes chrome/images/statusinactive.png | Bin 621 -> 0 bytes chrome/js/browserOverlay.js | 73 ----- chrome/js/component.js | 21 -- chrome/js/jquery-1.6.2.min.js | 18 -- chrome/js/redirect.js | 227 ---------------- chrome/js/redirector-ui.js | 354 ------------------------- chrome/js/redirector.js | 342 ------------------------ chrome/js/redirectorprefs.js | 76 ------ chrome/js/xpcom.js | 34 --- chrome/redirector.html | 85 ------ chrome/unittest/run.html | 98 ------- chrome/unittest/testcases.js | 129 --------- css/redirector.css | 341 ++++++++++++++++++++++++ defaults/preferences/redirector.preferences.js | 6 - help.html | 182 +++++++++++++ images/redirector.png | Bin 0 -> 1462 bytes images/statusactive.png | Bin 0 -> 340 bytes images/statusinactive.png | Bin 0 -> 621 bytes install.rdf | 27 -- js/angular.min.js | 293 ++++++++++++++++++++ js/app.js | 276 +++++++++++++++++++ js/background.js | 25 ++ js/redirect.js | 187 +++++++++++++ js/redirector.js | 342 ++++++++++++++++++++++++ locale/en-US/browserOverlay.dtd | 9 - locale/en-US/editRedirect.xul.dtd | 14 - locale/en-US/redirector.properties | 31 --- locale/en-US/settings.xul.dtd | 70 ----- manifest.json | 18 ++ redirector.html | 148 +++++++++++ unittest/run.html | 98 +++++++ unittest/testcases.js | 129 +++++++++ 38 files changed, 2039 insertions(+), 2001 deletions(-) delete mode 100644 chrome.manifest delete mode 100644 chrome/browserOverlay.xul delete mode 100644 chrome/css/redirector.css delete mode 100644 chrome/help.html delete mode 100644 chrome/images/redirector.png delete mode 100644 chrome/images/statusactive.png delete mode 100644 chrome/images/statusinactive.png delete mode 100644 chrome/js/browserOverlay.js delete mode 100644 chrome/js/component.js delete mode 100644 chrome/js/jquery-1.6.2.min.js delete mode 100644 chrome/js/redirect.js delete mode 100644 chrome/js/redirector-ui.js delete mode 100644 chrome/js/redirector.js delete mode 100644 chrome/js/redirectorprefs.js delete mode 100644 chrome/js/xpcom.js delete mode 100644 chrome/redirector.html delete mode 100644 chrome/unittest/run.html delete mode 100644 chrome/unittest/testcases.js create mode 100644 css/redirector.css delete mode 100644 defaults/preferences/redirector.preferences.js create mode 100644 help.html create mode 100644 images/redirector.png create mode 100644 images/statusactive.png create mode 100644 images/statusinactive.png delete mode 100644 install.rdf create mode 100644 js/angular.min.js create mode 100644 js/app.js create mode 100644 js/background.js create mode 100644 js/redirect.js create mode 100644 js/redirector.js delete mode 100644 locale/en-US/browserOverlay.dtd delete mode 100644 locale/en-US/editRedirect.xul.dtd delete mode 100644 locale/en-US/redirector.properties delete mode 100644 locale/en-US/settings.xul.dtd create mode 100644 manifest.json create mode 100644 redirector.html create mode 100644 unittest/run.html create mode 100644 unittest/testcases.js diff --git a/chrome.manifest b/chrome.manifest deleted file mode 100644 index 5221095..0000000 --- a/chrome.manifest +++ /dev/null @@ -1,9 +0,0 @@ -content redirector file:chrome/ -locale redirector en-US file:locale/en-US/ - -overlay chrome://browser/content/browser.xul chrome://redirector/content/browserOverlay.xul - -component {b7a7a54f-0581-47ff-b086-d6920cb7a3f7} chrome/js/component.js -contract @einaregilsson.com/redirector;1 {b7a7a54f-0581-47ff-b086-d6920cb7a3f7} -category content-policy Redirector @einaregilsson.com/redirector;1 -category net-channel-event-sinks Redirector @einaregilsson.com/redirector;1 \ No newline at end of file diff --git a/chrome/browserOverlay.xul b/chrome/browserOverlay.xul deleted file mode 100644 index f3ddff7..0000000 --- a/chrome/browserOverlay.xul +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - -

Redirector

-
Go where YOU want!
-
- - - - - -
- -
-

Settings

-
-
-
-
- -
-
-
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - Regular Expression - Wildcard -
-
- - -
-
- - -
-
- - -
-
- - - -
-
- - diff --git a/chrome/unittest/run.html b/chrome/unittest/run.html deleted file mode 100644 index 1c056d5..0000000 --- a/chrome/unittest/run.html +++ /dev/null @@ -1,98 +0,0 @@ - - - - Redirector Unit Tests - - - - - -

Redirector Unit Tests

- - -
-
- - \ No newline at end of file diff --git a/chrome/unittest/testcases.js b/chrome/unittest/testcases.js deleted file mode 100644 index 0c0c10c..0000000 --- a/chrome/unittest/testcases.js +++ /dev/null @@ -1,129 +0,0 @@ -//// $Id$ -var nsIContentPolicy = Components.interfaces.nsIContentPolicy; - -var tests = { - "Wildcard matches" : { - run : function(data,log) { - var pattern = data[0], - url = data[1], - expected = data[2]; - var parts = expected.split(','); - var redirectUrl = ''; - if (!(parts.length == 1 && parts[0] == '')) { - for (var i in parts) { - redirectUrl += '$' + (parseFloat(i)+1) + ','; - } - redirectUrl = redirectUrl.substr(0, redirectUrl.length-1); - } - var redirect = new Redirect(null, pattern, redirectUrl, Redirect.WILDCARD); - var result = redirect.getMatch(url); - return { passed: result.isMatch && (result.redirectTo == expected), message : "Expected '" + expected + "', actual was '" + result.redirectTo + "'"}; - }, - - describe : function(data) { return data[0] + ' == ' + data[1] + ', matches=' + data[2]; }, - tests : [ - ['http://foo*', 'http://foobar.is', 'bar.is'], - ['http://foo*', 'http://foo', ''], - ['*://foo.is', 'http://foo.is', 'http'], - ['*http://foo.is', 'http://foo.is', ''], - ['http*foo*', 'http://foobar.is', '://,bar.is'], - ['http*foo*', 'http://foo', '://,'], - ['*://f*.is', 'http://foo.is', 'http,oo'], - ['*http://f*.is', 'http://foo.is', ',oo'], - ['*foo*', 'http://foo', 'http://,'], - ['*foo*', 'foobar.is', ',bar.is'], - ['*foo*', 'http://foobar.is', 'http://,bar.is'], - ['http://foo.is', 'http://foo.is', ''], - ['*', 'http://foo.is', 'http://foo.is'], - ['*://*oo*bar*', 'http://foo.is/bar/baz', 'http,f,.is/,/baz'], - ['*://**oo*bar*', 'http://foo.is/bar/baz', 'http,,f,.is/,/baz'], - ] - }, - - "Regex matches" : { - run : function(data) { - var pattern = data[0], - url = data[1], - expected = data[2]; - var parts = expected.split(','); - var redirectUrl = ''; - if (!(parts.length == 1 && parts[0] == '')) { - for (var i in parts) { - redirectUrl += '$' + (parseFloat(i)+1) + ','; - } - redirectUrl = redirectUrl.substr(0, redirectUrl.length-1); - } - var redirect = new Redirect(null, pattern, redirectUrl, Redirect.REGEX, null, null); - var result = redirect.getMatch(url); - return { passed: result.isMatch && result.redirectTo == expected, message : "Expected '" + expected + "', actual was '" + result.redirectTo + "'"}; - }, - - describe : function(data) { return data[0] + ' == ' + data[1] + ', matches=' + data[2]; }, - tests : [ - ['http://foo(.*)', 'http://foobar.is', 'bar.is'], - ['http://foo(.*)', 'http://foo', ''], - ['(.*)://foo.is', 'http://foo.is', 'http'], - ['(.*)http://foo\\.is', 'http://foo.is', ''], - ['http(.*)foo(.*)', 'http://foobar.is', '://,bar.is'], - ['http(.*)foo(.*)', 'http://foo', '://,'], - ['(.*)://f(.*)\\.is', 'http://foo.is', 'http,oo'], - ['(.*)http://f(.*)\\.is', 'http://foo.is', ',oo'], - ['(.*)foo(.*)', 'http://foo', 'http://,'], - ['(.*)foo(.*)', 'foobar.is', ',bar.is'], - ['(.*)foo(.*)', 'http://foobar.is', 'http://,bar.is'], - ['http://foo\.is', 'http://foo.is', ''], - ['(.*)', 'http://foo.is', 'http://foo.is'], - ['(.*)://(.*)oo(.*)bar(.*)', 'http://foo.is/bar/baz', 'http,f,.is/,/baz'], - ['(.*)://(.*?)(.*)oo(.*)bar(.*)', 'http://foo.is/bar/baz', 'http,,f,.is/,/baz'], - ] - }, - - "nsIContentPolicy implementation" : { - run : function(data) { - var runTest = function() { - var args = { - contentType : nsIContentPolicy.TYPE_DOCUMENT, - contentLocation : "http://foo.is", - requestOrigin : null, - aContext : { loadURI : function(){}}, - mimeTypeGuess : null, - extra : null - }; - for (var key in data[1]) { - args[key] = data[1][key]; - } - - var ioService = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService); - args.contentLocation = ioService.newURI(args.contentLocation, null, null); - var contentPolicy = redirector.QueryInterface(nsIContentPolicy); - var result = contentPolicy.shouldLoad(args.contentType, args.contentLocation, args.requestOrigin, args.aContext, args.mimeTypeGuess, args.extra); - return { passed: result == nsIContentPolicy.ACCEPT, message : "Expected nsIContentPolicy.ACCEPT, actual was " + result }; - } - - if (typeof data[2] == "function") { - return data[2](runTest); - } else { - return runTest(); - } - }, - - describe : function(data) { return data[0]; }, - tests : [ - ["Accepts if not TYPE_DOCUMENT", { contentType : nsIContentPolicy.TYPE_STYLESHEET}], - ["Accepts if not http or https", { contentLocation : "resource://foo/bar"}], - ["Accepts if no aContext", { aContext : null}], - ["Accepts if aContext has no loadURI function", { aContext : { foo : function(){}}}], - ["Accepts if Redirector is not enabled", {}, function(doFunc) { - try { - redirector.enabled = false; - return doFunc(); - redirector.enabled = true; - - } catch(e) { - redirector.enabled = true; - throw e; - } - }] - ] - } -}; diff --git a/css/redirector.css b/css/redirector.css new file mode 100644 index 0000000..6997d32 --- /dev/null +++ b/css/redirector.css @@ -0,0 +1,341 @@ + +/* Basic element styles */ + +body { + font-family: Arial, sans-serif; + background:white; + color:rgb(43, 38, 38); +} + +h1, h2, h3, h5, h6 { + text-align: center; +} + +h1 { + font-size:55px; + margin:20px 0px 0px 0px; +} + +h3 { + font-size:18px; + padding:0px; +} + +h4 { + text-align: left; + font-size:14px; + padding:0px; + margin:4px; +} + +h5 { + font-size:20px; + margin:0; + color:#5e6163; +} + +input[type='text'] { + border-radius:2px; + padding:3px; + border:solid 1px #bbb; +} + +input[type="radio"] { + margin-right:5px; +} + +/* Classes for buttons and other stuff */ + +.btn { + background:white; + border:solid 1px #bbb; + border-radius:2px; + cursor: pointer; + font-weight: bold; + display:inline-block; + text-align: center; + text-decoration: none; +} + +.btn.large { + font-size:18px; + padding:4px 8px; + +} + +.btn.medium { + font-size:14px; + padding:2px 6px; +} + +.btn.small { + +} + +.btn:hover { + color:white !important; + text-decoration: none; +} + + +.btn.grey { + color:#333; +} + +.btn.grey:hover { + background:#333; +} + +.btn.red { + color:rgb(208,52,37); +} + +.btn.red:hover { + background:rgb(208,52,37); +} + +.btn.blue { + color:rgb(21,90,233); +} + +.btn.blue:hover { + background:rgb(21,90,233); +} + +.btn.green { + color:green; +} + +.btn.green:hover { + background:green; +} + +/* Main menu with buttons */ + +#menu { + margin: 30px auto 8px auto; + max-width:90%; + text-align: center; +} + +#import-file { + display:none; +} + +/* Message box for success/failure messages */ +#message-box { + margin:10px auto; + width:90%; + color:white; + max-width: 800px; + border-radius:3px; + height:0px; + line-height: 30px; + font-size:16px; + text-align:center; + overflow:hidden; + transition: height 0.2s ease-out; + position: relative; +} + +#message-box.visible { + height:30px; + transition: height 0.2s ease-out; +} + +#message-box.success { + background-color: green; +} + +#message-box.error { + background-color: rgb(208,52,37);; +} + +#message-box a { + color:white; + font-size:20px; + position: absolute; + right:6px; + top:0px; +} + +/* Table of redirects */ + +.redirect-table { + width:90%; + max-width: 800px; + border:solid 1px #bbb; + margin:0px auto; + border-radius:3px; +} + +.redirect-row { + position: relative; + font-size:13px; + padding:8px; + line-height:18px; + border-bottom:solid 1px #bbb; +} + +.redirect-row:last-child { + border-bottom: none !important; +} + +.redirect-row:nth-child(odd) { + background:#f8f8f8; +} + +.redirect-row.disabled label, .redirect-row.disabled span, a.disabled { + color:#bbb !important; +} + +/* Edit, Delete, Disable buttons */ +.redirect-row a, .redirect-row a:visited { + font-size:13px; + margin-top:5px; + width:60px; +} + +#delete-redirect-form { + position: fixed; + width:500px; + background:white; + border:solid 1px #bbb; + border-radius:3px; + z-index: 6000; + left:50%; + margin-left:-250px; + top:50%; + margin-top:-220px; + height:220px; +} + +#delete-redirect-form div{ + margin-bottom:7px; +} + +#delete-redirect-form div label:first-child { + width:130px; + font-weight:bold; + text-align: right; + display:inline-block; +} + +.redirect-row h4 span.disabled-marker { + color:red !important; +} + +.move-up-btn, .move-down-btn { + width:35px !important; +} + + +.redirect-row label { + display:inline-block; + width:80px; + font-weight:bold; + text-align: right; +} + +a.disabled:hover { + cursor:default; + color:#bbb !important; + background:white !important; +} + +/* Edit form */ + +#cover { + position: fixed; + top:0px; + bottom:0px; + left:0px; + right:0px; + z-index: 5000; + background: #333; + opacity: 0.5; +} + +.blur { + -webkit-filter:blur(3px); + -moz-filter:blur(3px); +} + +#edit-redirect-form { + position: fixed; + width:500px; + background:white; + border:solid 1px #bbb; + border-radius:3px; + z-index: 6000; + left:50%; + margin-left:-250px; + top:50%; + margin-top:-258px; +} + +#edit-redirect-form div{ + margin-bottom:7px; +} + +#edit-redirect-form div label:first-child { + width:130px; + font-weight:bold; + text-align: right; + display:inline-block; +} + +#edit-redirect-form .second-column { + display:block; + padding-left: 134px; +} + +#edit-redirect-form div input[type='text'] { + width:300px; +} + +#advanced-toggle { + text-align: center; +} + +#advanced-toggle a { + color:rgb(21,90,233); +} + +#advanced-toggle a:hover { + text-decoration: underline; +} + + +a[ng-click] { + cursor:pointer; +} + +#wildcardtype { + margin-right:10px; +} + +.button-container { + margin-top:20px; + text-align: center; +} + +/* Footer with link */ +footer { + margin-top:30px; + text-align: center; +} + +footer small { + font-size:10px; + color:#555; +} + +footer small a, footer small a:visited { + text-decoration: none; + color:rgb(21,90,233); +} + +footer small a:hover { + text-decoration: underline; +} \ No newline at end of file diff --git a/defaults/preferences/redirector.preferences.js b/defaults/preferences/redirector.preferences.js deleted file mode 100644 index e7cc381..0000000 --- a/defaults/preferences/redirector.preferences.js +++ /dev/null @@ -1,6 +0,0 @@ - -pref("extensions.redirector.debugEnabled", false); -pref("extensions.redirector.enabled", true); -pref("extensions.redirector.enableShortcutKey", true); -pref("extensions.redirector.version", 'undefined'); -pref("extensions.redirector.defaultDir", ''); diff --git a/help.html b/help.html new file mode 100644 index 0000000..a6a589b --- /dev/null +++ b/help.html @@ -0,0 +1,182 @@ + + + + Redirector Help + + + +

Redirector Help

+

Table of contents

+ + + + +

What is Redirector?

+ +

Redirector is an extension for Firefox that allows you to automatically redirect from + one webpage to another. For example, every time you visit http://abc.com you will automatically + load http://def.com instead. This can be useful for instance to always redirect articles to printer friendly + versions, redirect http:// to https:// for sites that support both, bypass advertising pages that appear before + being able to view certain pages and more.

+ + +

Basic usage

+

To add a new redirect you can go to the Tools menuitem and select Redirector. That will + open the Redirector settings window which shows all your redirects. The window can also be opened + by right clicking on the R icon in your statusbar. + There you can press the Add... button and then you can enter the details for the new redirect. A redirect + consists of a few things: +

+

+ + +

Wildcards

+ +

Wildcards are the simplest way to specify include and exclude patterns. When you create a wildcard pattern there + is just one special character, the asterisk *. An asterisk in your pattern will match zero or more characters and you can + have more than one star in your pattern. Some examples: +

+ $1, $2, $3 in the redirect urls will match the text that the stars matched. Examples: + +

+ + +

Regular expressions

+ +

Regular expressions allow for more complicated patterns but they are a lot harder to learn than wildcards. I'm not gonna + create a regex tutorial here but normal javascript regex syntax works, look at http://regular-expressions.info for + an introduction to regular expressions. $1,$2 etc. can be used in the redirect url and will be replaced with contents of captures in + the regular expressions. Captures are specified with parantheses. Example: http://example.com/index.asp\?id=(\d+) will match the url + http://example.com/index.asp?id=12345 and $1 will be replaced by 12345. (A common mistake in regex patterns is to forget to escape + the ? sign in the querystring of the url. ? is a special character in regular expressions so if you want to match an url with a querystring + you should escape it as \?).

+ + +

Examples

+ +
    +
  1. + Static redirect
    + Redirects from http://example.com/foo to http://example.com/bar +

    + Include pattern: http://example.com/foo
    + Exclude pattern:
    + Redirect to: http://example.com/bar
    + Pattern type: Wildcard
    +

    + +
  2. +
  3. + Redirect using query string parameter and wildcards
    + Redirects from http://example.com/index.php?id=12345&a=b to http://example.com/printerfriendly.php?id=12345&a=b + where 12345 could be any number. +

    + Include pattern: http://example.com/index.php?id=*&a=b
    + Exclude pattern:
    + Redirect to: http://example.com/printerfriendly.com?id=$1&a=b
    + Pattern type: Wildcard
    +

    +
  4. +
  5. + Redirect using query string parameter and regular expressions
    + Redirects from http://example.com/index.php?id=12345&a=b to http://example.com/printerfriendly.php?id=12345&a=b + where 12345 could be any number. +

    + Include pattern: http://example.com/index.php\?id=(\d+)&a=b
    + Exclude pattern:
    + Redirect to: http://example.com/printerfriendly.com?id=$1&a=b
    + Pattern type: Regular Expression
    +

    +
  6. +
  7. + Redirect to a different folder using wildcards
    + Redirects from http://example.com/category/fish/index.php to http://example.com/category/cats/index.php + where fish could be any word. The exclude pattern makes sure that there is only one + folder there, so for instance http://example.com/category/fish/cat/mouse/index.php would not match. +

    + Include pattern: http://example.com/category/*/index.php
    + Exclude pattern: http://example.com/category/*/*/index.php
    + Redirect to: http://example.com/category/cats/index.php
    + Pattern type: Wildcard
    +

    +
  8. +
  9. + Redirect http to https using wildcards
    + Redirects from http://mail.google.com/randomcharacters to https://mail.google.com/randomcharacters + where randomcharacters could be anything. +

    + Include pattern: http://mail.google.com*
    + Exclude pattern:
    + Redirect to: https://mail.google.com$1
    + Pattern type: Wildcard
    +

    +
  10. +
+ + \ No newline at end of file diff --git a/images/redirector.png b/images/redirector.png new file mode 100644 index 0000000..f8de12c Binary files /dev/null and b/images/redirector.png differ diff --git a/images/statusactive.png b/images/statusactive.png new file mode 100644 index 0000000..3127229 Binary files /dev/null and b/images/statusactive.png differ diff --git a/images/statusinactive.png b/images/statusinactive.png new file mode 100644 index 0000000..4c0438e Binary files /dev/null and b/images/statusinactive.png differ diff --git a/install.rdf b/install.rdf deleted file mode 100644 index 6ab60a0..0000000 --- a/install.rdf +++ /dev/null @@ -1,27 +0,0 @@ - - - - redirector@einaregilsson.com - Redirector - 2.9.3 - Einar Egilsson - Noah Luck Easterly - https://github.com/gitoffthelawn - https://github.com/Frogomeli - Automatically redirects to user-defined urls on certain pages - http://einaregilsson.com/redirector/ - chrome://redirector/content/redirector.html - 3 - chrome://redirector/content/images/redirector.png - 2 - true - - - {ec8030f7-c20a-464f-9b0e-13a3a9e97384} - 17.0 - 38.* - - - - \ No newline at end of file diff --git a/js/angular.min.js b/js/angular.min.js new file mode 100644 index 0000000..85409f6 --- /dev/null +++ b/js/angular.min.js @@ -0,0 +1,293 @@ +/* + AngularJS v1.4.4 + (c) 2010-2015 Google, Inc. http://angularjs.org + License: MIT +*/ +(function(O,W,t){'use strict';function L(b){return function(){var a=arguments[0],c;c="["+(b?b+":":"")+a+"] http://errors.angularjs.org/1.4.4/"+(b?b+"/":"")+a;for(a=1;a").append(b).html();try{return b[0].nodeType===Pa?M(c):c.match(/^(<[^>]+>)/)[1].replace(/^<([\w\-]+)/,function(a,b){return"<"+M(b)})}catch(d){return M(c)}}function wc(b){try{return decodeURIComponent(b)}catch(a){}}function xc(b){var a={};m((b||"").split("&"),function(b){var d,e,f;b&&(e= +b=b.replace(/\+/g,"%20"),d=b.indexOf("="),-1!==d&&(e=b.substring(0,d),f=b.substring(d+1)),e=wc(e),x(e)&&(f=x(f)?wc(f):!0,Na.call(a,e)?G(a[e])?a[e].push(f):a[e]=[a[e],f]:a[e]=f))});return a}function Pb(b){var a=[];m(b,function(b,d){G(b)?m(b,function(b){a.push(ma(d,!0)+(!0===b?"":"="+ma(b,!0)))}):a.push(ma(d,!0)+(!0===b?"":"="+ma(b,!0)))});return a.length?a.join("&"):""}function nb(b){return ma(b,!0).replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+")}function ma(b,a){return encodeURIComponent(b).replace(/%40/gi, +"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%3B/gi,";").replace(/%20/g,a?"%20":"+")}function Yd(b,a){var c,d,e=Qa.length;for(d=0;d/,">"));}a=a||[];a.unshift(["$provide",function(a){a.value("$rootElement",b)}]);c.debugInfoEnabled&&a.push(["$compileProvider",function(a){a.debugInfoEnabled(!0)}]);a.unshift("ng");d=eb(a,c.strictDi);d.invoke(["$rootScope","$rootElement","$compile","$injector",function(a,b,c,d){a.$apply(function(){b.data("$injector", +d);c(b)(a)})}]);return d},e=/^NG_ENABLE_DEBUG_INFO!/,f=/^NG_DEFER_BOOTSTRAP!/;O&&e.test(O.name)&&(c.debugInfoEnabled=!0,O.name=O.name.replace(e,""));if(O&&!f.test(O.name))return d();O.name=O.name.replace(f,"");aa.resumeBootstrap=function(b){m(b,function(b){a.push(b)});return d()};C(aa.resumeDeferredBootstrap)&&aa.resumeDeferredBootstrap()}function $d(){O.name="NG_ENABLE_DEBUG_INFO!"+O.name;O.location.reload()}function ae(b){b=aa.element(b).injector();if(!b)throw Ea("test");return b.get("$$testability")} +function zc(b,a){a=a||"_";return b.replace(be,function(b,d){return(d?a:"")+b.toLowerCase()})}function ce(){var b;if(!Ac){var a=ob();la=O.jQuery;x(a)&&(la=null===a?t:O[a]);la&&la.fn.on?(z=la,Q(la.fn,{scope:Ra.scope,isolateScope:Ra.isolateScope,controller:Ra.controller,injector:Ra.injector,inheritedData:Ra.inheritedData}),b=la.cleanData,la.cleanData=function(a){var d;if(Qb)Qb=!1;else for(var e=0,f;null!=(f=a[e]);e++)(d=la._data(f,"events"))&&d.$destroy&&la(f).triggerHandler("$destroy");b(a)}):z=R;aa.element= +z;Ac=!0}}function pb(b,a,c){if(!b)throw Ea("areq",a||"?",c||"required");return b}function Sa(b,a,c){c&&G(b)&&(b=b[b.length-1]);pb(C(b),a,"not a function, got "+(b&&"object"===typeof b?b.constructor.name||"Object":typeof b));return b}function Ta(b,a){if("hasOwnProperty"===b)throw Ea("badname",a);}function Bc(b,a,c){if(!a)return b;a=a.split(".");for(var d,e=b,f=a.length,g=0;g")+d[2];for(d=d[0];d--;)c=c.lastChild;f=cb(f,c.childNodes);c=e.firstChild;c.textContent=""}else f.push(a.createTextNode(b));e.textContent="";e.innerHTML="";m(f,function(a){e.appendChild(a)});return e}function R(b){if(b instanceof R)return b;var a;I(b)&&(b=T(b),a=!0);if(!(this instanceof R)){if(a&&"<"!=b.charAt(0))throw Tb("nosel");return new R(b)}if(a){a=W;var c;b=(c=Df.exec(b))?[a.createElement(c[1])]: +(c=Lc(b,a))?c.childNodes:[]}Mc(this,b)}function Ub(b){return b.cloneNode(!0)}function tb(b,a){a||ub(b);if(b.querySelectorAll)for(var c=b.querySelectorAll("*"),d=0,e=c.length;dk&&this.remove(r.key); +return b}},get:function(a){if(k").parent()[0])});var f=S(a,b,a,c,d,e);V.$$addScopeClass(a);var g=null;return function(b,c,d){pb(b,"scope");d=d||{}; +var e=d.parentBoundTranscludeFn,h=d.transcludeControllers;d=d.futureParentElement;e&&e.$$boundTransclude&&(e=e.$$boundTransclude);g||(g=(d=d&&d[0])?"foreignobject"!==ta(d)&&d.toString().match(/SVG/)?"svg":"html":"html");d="html"!==g?z(Xb(g,z("
").append(a).html())):c?Ra.clone.call(a):a;if(h)for(var l in h)d.data("$"+l+"Controller",h[l].instance);V.$$addScopeInfo(d,b);c&&c(d,b);f&&f(b,d,d,e);return d}}function S(a,b,c,d,e,f){function g(a,c,d,e){var f,l,k,r,n,u,N;if(p)for(N=Array(c.length),r=0;r< +h.length;r+=3)f=h[r],N[f]=c[f];else N=c;r=0;for(n=h.length;rE.priority)break;if(v=E.scope)E.templateUrl||(D(v)?(O("new/isolated scope",P||S,E,ba),P=E):O("new/isolated scope", +P,E,ba)),S=S||E;x=E.name;!E.templateUrl&&E.controller&&(v=E.controller,w=w||ga(),O("'"+x+"' controller",w[x],E,ba),w[x]=E);if(v=E.transclude)m=!0,E.$$tlb||(O("transclusion",A,E,ba),A=E),"element"==v?(s=!0,J=E.priority,v=ba,ba=d.$$element=z(W.createComment(" "+x+": "+d[x]+" ")),b=ba[0],U(f,xa.call(v,0),b),y=V(v,e,J,g&&g.name,{nonTlbTranscludeDirective:A})):(v=z(Ub(b)).contents(),ba.empty(),y=V(v,e));if(E.template)if(H=!0,O("template",F,E,ba),F=E,v=C(E.template)?E.template(ba,d):E.template,v=fa(v), +E.replace){g=E;v=Sb.test(v)?Yc(Xb(E.templateNamespace,T(v))):[];b=v[0];if(1!=v.length||b.nodeType!==pa)throw ea("tplrt",x,"");U(f,ba,b);L={$attr:{}};v=ha(b,[],L);var Q=a.splice(wa+1,a.length-(wa+1));P&&Zc(v);a=a.concat(v).concat(Q);$c(d,L);L=a.length}else ba.html(v);if(E.templateUrl)H=!0,O("template",F,E,ba),F=E,E.replace&&(g=E),K=Lf(a.splice(wa,a.length-wa),ba,d,f,m&&y,h,k,{controllerDirectives:w,newScopeDirective:S!==E&&S,newIsolateScopeDirective:P,templateDirective:F,nonTlbTranscludeDirective:A}), +L=a.length;else if(E.compile)try{za=E.compile(ba,d,y),C(za)?n(null,za,Bb,M):za&&n(za.pre,za.post,Bb,M)}catch(R){c(R,ua(ba))}E.terminal&&(K.terminal=!0,J=Math.max(J,E.priority))}K.scope=S&&!0===S.scope;K.transcludeOnThisElement=m;K.templateOnThisElement=H;K.transclude=y;r.hasElementTranscludeDirective=s;return K}function Zc(a){for(var b=0,c=a.length;bn.priority)&&-1!=n.restrict.indexOf(f)&&(l&&(n=Nb(n,{$$start:l,$$end:k})),b.push(n),h=n)}catch(J){c(J)}}return h}function L(b){if(e.hasOwnProperty(b))for(var c=a.get(b+"Directive"),d=0,f=c.length;d"+b+"";return c.childNodes[0].childNodes;default:return b}}function R(a,b){if("srcdoc"==b)return H.HTML;var c=ta(a);if("xlinkHref"==b||"form"==c&&"action"==b||"img"!=c&&("src"==b||"ngSrc"==b))return H.RESOURCE_URL}function X(a,c,d,e, +f){var g=R(a,e);f=h[e]||f;var l=b(d,!0,g,f);if(l){if("multiple"===e&&"select"===ta(a))throw ea("selmulti",ua(a));c.push({priority:100,compile:function(){return{pre:function(a,c,h){c=h.$$observers||(h.$$observers={});if(k.test(e))throw ea("nodomevents");var r=h[e];r!==d&&(l=r&&b(r,!0,g,f),d=r);l&&(h[e]=l(a),(c[e]||(c[e]=[])).$$inter=!0,(h.$$observers&&h.$$observers[e].$$scope||a).$watch(l,function(a,b){"class"===e&&a!=b?h.$updateClass(a,b):h.$set(e,a)}))}}}})}}function U(a,b,c){var d=b[0],e=b.length, +f=d.parentNode,g,h;if(a)for(g=0,h=a.length;g=a)return b;for(;a--;)8===b[a].nodeType&&Mf.call(b,a,1);return b}function Xe(){var b={},a=!1;this.register=function(a,d){Ta(a,"controller");D(a)?Q(b,a):b[a]=d};this.allowGlobals=function(){a=!0};this.$get=["$injector","$window",function(c,d){function e(a,b,c,d){if(!a||!D(a.$scope))throw L("$controller")("noscp",d,b);a.$scope[b]=c}return function(f,g,h,l){var k,n,q;h=!0===h;l&&I(l)&&(q=l);if(I(f)){l=f.match(Vc);if(!l)throw Nf("ctrlfmt",f);n=l[1];q=q||l[3];f=b.hasOwnProperty(n)?b[n]:Bc(g.$scope, +n,!0)||(a?Bc(d,n,!0):t);Sa(f,n,!0)}if(h)return h=(G(f)?f[f.length-1]:f).prototype,k=Object.create(h||null),q&&e(g,q,k,n||f.name),Q(function(){var a=c.invoke(f,k,g,n);a!==k&&(D(a)||C(a))&&(k=a,q&&e(g,q,k,n||f.name));return k},{instance:k,identifier:q});k=c.instantiate(f,g,n);q&&e(g,q,k,n||f.name);return k}}]}function Ye(){this.$get=["$window",function(b){return z(b.document)}]}function Ze(){this.$get=["$log",function(b){return function(a,c){b.error.apply(b,arguments)}}]}function Yb(b){return D(b)? +ca(b)?b.toISOString():db(b):b}function df(){this.$get=function(){return function(b){if(!b)return"";var a=[];mc(b,function(b,d){null===b||y(b)||(G(b)?m(b,function(b,c){a.push(ma(d)+"="+ma(Yb(b)))}):a.push(ma(d)+"="+ma(Yb(b))))});return a.join("&")}}}function ef(){this.$get=function(){return function(b){function a(b,e,f){null===b||y(b)||(G(b)?m(b,function(b,c){a(b,e+"["+(D(b)?c:"")+"]")}):D(b)&&!ca(b)?mc(b,function(b,c){a(b,e+(f?"":"[")+c+(f?"":"]"))}):c.push(ma(e)+"="+ma(Yb(b))))}if(!b)return"";var c= +[];a(b,"",!0);return c.join("&")}}}function Zb(b,a){if(I(b)){var c=b.replace(Of,"").trim();if(c){var d=a("Content-Type");(d=d&&0===d.indexOf(bd))||(d=(d=c.match(Pf))&&Qf[d[0]].test(c));d&&(b=uc(c))}}return b}function cd(b){var a=ga(),c;I(b)?m(b.split("\n"),function(b){c=b.indexOf(":");var e=M(T(b.substr(0,c)));b=T(b.substr(c+1));e&&(a[e]=a[e]?a[e]+", "+b:b)}):D(b)&&m(b,function(b,c){var f=M(c),g=T(b);f&&(a[f]=a[f]?a[f]+", "+g:g)});return a}function dd(b){var a;return function(c){a||(a=cd(b));return c? +(c=a[M(c)],void 0===c&&(c=null),c):a}}function ed(b,a,c,d){if(C(d))return d(b,a,c);m(d,function(d){b=d(b,a,c)});return b}function cf(){var b=this.defaults={transformResponse:[Zb],transformRequest:[function(a){return D(a)&&"[object File]"!==sa.call(a)&&"[object Blob]"!==sa.call(a)&&"[object FormData]"!==sa.call(a)?db(a):a}],headers:{common:{Accept:"application/json, text/plain, */*"},post:ia($b),put:ia($b),patch:ia($b)},xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",paramSerializer:"$httpParamSerializer"}, +a=!1;this.useApplyAsync=function(b){return x(b)?(a=!!b,this):a};var c=!0;this.useLegacyPromiseExtensions=function(a){return x(a)?(c=!!a,this):c};var d=this.interceptors=[];this.$get=["$httpBackend","$$cookieReader","$cacheFactory","$rootScope","$q","$injector",function(e,f,g,h,l,k){function n(a){function d(a){var b=Q({},a);b.data=a.data?ed(a.data,a.headers,a.status,f.transformResponse):a.data;a=a.status;return 200<=a&&300>a?b:l.reject(b)}function e(a,b){var c,d={};m(a,function(a,e){C(a)?(c=a(b),null!= +c&&(d[e]=c)):d[e]=a});return d}if(!aa.isObject(a))throw L("$http")("badreq",a);var f=Q({method:"get",transformRequest:b.transformRequest,transformResponse:b.transformResponse,paramSerializer:b.paramSerializer},a);f.headers=function(a){var c=b.headers,d=Q({},a.headers),f,g,h,c=Q({},c.common,c[M(a.method)]);a:for(f in c){g=M(f);for(h in d)if(M(h)===g)continue a;d[f]=c[f]}return e(d,ia(a))}(a);f.method=rb(f.method);f.paramSerializer=I(f.paramSerializer)?k.get(f.paramSerializer):f.paramSerializer;var g= +[function(a){var c=a.headers,e=ed(a.data,dd(c),t,a.transformRequest);y(e)&&m(c,function(a,b){"content-type"===M(b)&&delete c[b]});y(a.withCredentials)&&!y(b.withCredentials)&&(a.withCredentials=b.withCredentials);return q(a,e).then(d,d)},t],h=l.when(f);for(m(B,function(a){(a.request||a.requestError)&&g.unshift(a.request,a.requestError);(a.response||a.responseError)&&g.push(a.response,a.responseError)});g.length;){a=g.shift();var r=g.shift(),h=h.then(a,r)}c?(h.success=function(a){Sa(a,"fn");h.then(function(b){a(b.data, +b.status,b.headers,f)});return h},h.error=function(a){Sa(a,"fn");h.then(null,function(b){a(b.data,b.status,b.headers,f)});return h}):(h.success=fd("success"),h.error=fd("error"));return h}function q(c,d){function g(b,c,d,e){function f(){k(c,b,d,e)}F&&(200<=b&&300>b?F.put(P,[b,c,cd(d),e]):F.remove(P));a?h.$applyAsync(f):(f(),h.$$phase||h.$apply())}function k(a,b,d,e){b=Math.max(b,0);(200<=b&&300>b?N.resolve:N.reject)({data:a,status:b,headers:dd(d),config:c,statusText:e})}function q(a){k(a.data,a.status, +ia(a.headers()),a.statusText)}function B(){var a=n.pendingRequests.indexOf(c);-1!==a&&n.pendingRequests.splice(a,1)}var N=l.defer(),J=N.promise,F,m,S=c.headers,P=r(c.url,c.paramSerializer(c.params));n.pendingRequests.push(c);J.then(B,B);!c.cache&&!b.cache||!1===c.cache||"GET"!==c.method&&"JSONP"!==c.method||(F=D(c.cache)?c.cache:D(b.cache)?b.cache:u);F&&(m=F.get(P),x(m)?m&&C(m.then)?m.then(q,q):G(m)?k(m[1],m[0],ia(m[2]),m[3]):k(m,200,{},"OK"):F.put(P,J));y(m)&&((m=gd(c.url)?f()[c.xsrfCookieName|| +b.xsrfCookieName]:t)&&(S[c.xsrfHeaderName||b.xsrfHeaderName]=m),e(c.method,P,d,g,S,c.timeout,c.withCredentials,c.responseType));return J}function r(a,b){0=l&&(w.resolve(B),u(p.$$intervalId),delete f[p.$$intervalId]);K||b.$apply()},h);f[p.$$intervalId]=w;return p}var f={};e.cancel=function(b){return b&& +b.$$intervalId in f?(f[b.$$intervalId].reject("canceled"),a.clearInterval(b.$$intervalId),delete f[b.$$intervalId],!0):!1};return e}]}function ac(b){b=b.split("/");for(var a=b.length;a--;)b[a]=nb(b[a]);return b.join("/")}function hd(b,a){var c=Aa(b);a.$$protocol=c.protocol;a.$$host=c.hostname;a.$$port=Y(c.port)||Tf[c.protocol]||null}function id(b,a){var c="/"!==b.charAt(0);c&&(b="/"+b);var d=Aa(b);a.$$path=decodeURIComponent(c&&"/"===d.pathname.charAt(0)?d.pathname.substring(1):d.pathname);a.$$search= +xc(d.search);a.$$hash=decodeURIComponent(d.hash);a.$$path&&"/"!=a.$$path.charAt(0)&&(a.$$path="/"+a.$$path)}function ra(b,a){if(0===a.indexOf(b))return a.substr(b.length)}function Ja(b){var a=b.indexOf("#");return-1==a?b:b.substr(0,a)}function Cb(b){return b.replace(/(#.+)|#$/,"$1")}function bc(b,a,c){this.$$html5=!0;c=c||"";hd(b,this);this.$$parse=function(b){var c=ra(a,b);if(!I(c))throw Db("ipthprfx",b,a);id(c,this);this.$$path||(this.$$path="/");this.$$compose()};this.$$compose=function(){var b= +Pb(this.$$search),c=this.$$hash?"#"+nb(this.$$hash):"";this.$$url=ac(this.$$path)+(b?"?"+b:"")+c;this.$$absUrl=a+this.$$url.substr(1)};this.$$parseLinkUrl=function(d,e){if(e&&"#"===e[0])return this.hash(e.slice(1)),!0;var f,g;(f=ra(b,d))!==t?(g=f,g=(f=ra(c,f))!==t?a+(ra("/",f)||f):b+g):(f=ra(a,d))!==t?g=a+f:a==d+"/"&&(g=a);g&&this.$$parse(g);return!!g}}function cc(b,a,c){hd(b,this);this.$$parse=function(d){var e=ra(b,d)||ra(a,d),f;y(e)||"#"!==e.charAt(0)?this.$$html5?f=e:(f="",y(e)&&(b=d,this.replace())): +(f=ra(c,e),y(f)&&(f=e));id(f,this);d=this.$$path;var e=b,g=/^\/[A-Z]:(\/.*)/;0===f.indexOf(e)&&(f=f.replace(e,""));g.exec(f)||(d=(f=g.exec(d))?f[1]:d);this.$$path=d;this.$$compose()};this.$$compose=function(){var a=Pb(this.$$search),e=this.$$hash?"#"+nb(this.$$hash):"";this.$$url=ac(this.$$path)+(a?"?"+a:"")+e;this.$$absUrl=b+(this.$$url?c+this.$$url:"")};this.$$parseLinkUrl=function(a,c){return Ja(b)==Ja(a)?(this.$$parse(a),!0):!1}}function jd(b,a,c){this.$$html5=!0;cc.apply(this,arguments);this.$$parseLinkUrl= +function(d,e){if(e&&"#"===e[0])return this.hash(e.slice(1)),!0;var f,g;b==Ja(d)?f=d:(g=ra(a,d))?f=b+c+g:a===d+"/"&&(f=a);f&&this.$$parse(f);return!!f};this.$$compose=function(){var a=Pb(this.$$search),e=this.$$hash?"#"+nb(this.$$hash):"";this.$$url=ac(this.$$path)+(a?"?"+a:"")+e;this.$$absUrl=b+c+this.$$url}}function Eb(b){return function(){return this[b]}}function kd(b,a){return function(c){if(y(c))return this[b];this[b]=a(c);this.$$compose();return this}}function gf(){var b="",a={enabled:!1,requireBase:!0, +rewriteLinks:!0};this.hashPrefix=function(a){return x(a)?(b=a,this):b};this.html5Mode=function(b){return ab(b)?(a.enabled=b,this):D(b)?(ab(b.enabled)&&(a.enabled=b.enabled),ab(b.requireBase)&&(a.requireBase=b.requireBase),ab(b.rewriteLinks)&&(a.rewriteLinks=b.rewriteLinks),this):a};this.$get=["$rootScope","$browser","$sniffer","$rootElement","$window",function(c,d,e,f,g){function h(a,b,c){var e=k.url(),f=k.$$state;try{d.url(a,b,c),k.$$state=d.state()}catch(g){throw k.url(e),k.$$state=f,g;}}function l(a, +b){c.$broadcast("$locationChangeSuccess",k.absUrl(),a,k.$$state,b)}var k,n;n=d.baseHref();var q=d.url(),r;if(a.enabled){if(!n&&a.requireBase)throw Db("nobase");r=q.substring(0,q.indexOf("/",q.indexOf("//")+2))+(n||"/");n=e.history?bc:jd}else r=Ja(q),n=cc;var u=r.substr(0,Ja(r).lastIndexOf("/")+1);k=new n(r,u,"#"+b);k.$$parseLinkUrl(q,q);k.$$state=d.state();var B=/^\s*(javascript|mailto):/i;f.on("click",function(b){if(a.rewriteLinks&&!b.ctrlKey&&!b.metaKey&&!b.shiftKey&&2!=b.which&&2!=b.button){for(var e= +z(b.target);"a"!==ta(e[0]);)if(e[0]===f[0]||!(e=e.parent())[0])return;var h=e.prop("href"),l=e.attr("href")||e.attr("xlink:href");D(h)&&"[object SVGAnimatedString]"===h.toString()&&(h=Aa(h.animVal).href);B.test(h)||!h||e.attr("target")||b.isDefaultPrevented()||!k.$$parseLinkUrl(h,l)||(b.preventDefault(),k.absUrl()!=d.url()&&(c.$apply(),g.angular["ff-684208-preventDefault"]=!0))}});Cb(k.absUrl())!=Cb(q)&&d.url(k.absUrl(),!0);var K=!0;d.onUrlChange(function(a,b){y(ra(u,a))?g.location.href=a:(c.$evalAsync(function(){var d= +k.absUrl(),e=k.$$state,f;k.$$parse(a);k.$$state=b;f=c.$broadcast("$locationChangeStart",a,d,b,e).defaultPrevented;k.absUrl()===a&&(f?(k.$$parse(d),k.$$state=e,h(d,!1,e)):(K=!1,l(d,e)))}),c.$$phase||c.$digest())});c.$watch(function(){var a=Cb(d.url()),b=Cb(k.absUrl()),f=d.state(),g=k.$$replace,n=a!==b||k.$$html5&&e.history&&f!==k.$$state;if(K||n)K=!1,c.$evalAsync(function(){var b=k.absUrl(),d=c.$broadcast("$locationChangeStart",b,a,k.$$state,f).defaultPrevented;k.absUrl()===b&&(d?(k.$$parse(a),k.$$state= +f):(n&&h(b,g,f===k.$$state?null:k.$$state),l(a,f)))});k.$$replace=!1});return k}]}function hf(){var b=!0,a=this;this.debugEnabled=function(a){return x(a)?(b=a,this):b};this.$get=["$window",function(c){function d(a){a instanceof Error&&(a.stack?a=a.message&&-1===a.stack.indexOf(a.message)?"Error: "+a.message+"\n"+a.stack:a.stack:a.sourceURL&&(a=a.message+"\n"+a.sourceURL+":"+a.line));return a}function e(a){var b=c.console||{},e=b[a]||b.log||v;a=!1;try{a=!!e.apply}catch(l){}return a?function(){var a= +[];m(arguments,function(b){a.push(d(b))});return e.apply(b,a)}:function(a,b){e(a,null==b?"":b)}}return{log:e("log"),info:e("info"),warn:e("warn"),error:e("error"),debug:function(){var c=e("debug");return function(){b&&c.apply(a,arguments)}}()}}]}function Wa(b,a){if("__defineGetter__"===b||"__defineSetter__"===b||"__lookupGetter__"===b||"__lookupSetter__"===b||"__proto__"===b)throw da("isecfld",a);return b}function Ba(b,a){if(b){if(b.constructor===b)throw da("isecfn",a);if(b.window===b)throw da("isecwindow", +a);if(b.children&&(b.nodeName||b.prop&&b.attr&&b.find))throw da("isecdom",a);if(b===Object)throw da("isecobj",a);}return b}function ld(b,a){if(b){if(b.constructor===b)throw da("isecfn",a);if(b===Uf||b===Vf||b===Wf)throw da("isecff",a);}}function Xf(b,a){return"undefined"!==typeof b?b:a}function md(b,a){return"undefined"===typeof b?a:"undefined"===typeof a?b:b+a}function U(b,a){var c,d;switch(b.type){case s.Program:c=!0;m(b.body,function(b){U(b.expression,a);c=c&&b.expression.constant});b.constant= +c;break;case s.Literal:b.constant=!0;b.toWatch=[];break;case s.UnaryExpression:U(b.argument,a);b.constant=b.argument.constant;b.toWatch=b.argument.toWatch;break;case s.BinaryExpression:U(b.left,a);U(b.right,a);b.constant=b.left.constant&&b.right.constant;b.toWatch=b.left.toWatch.concat(b.right.toWatch);break;case s.LogicalExpression:U(b.left,a);U(b.right,a);b.constant=b.left.constant&&b.right.constant;b.toWatch=b.constant?[]:[b];break;case s.ConditionalExpression:U(b.test,a);U(b.alternate,a);U(b.consequent, +a);b.constant=b.test.constant&&b.alternate.constant&&b.consequent.constant;b.toWatch=b.constant?[]:[b];break;case s.Identifier:b.constant=!1;b.toWatch=[b];break;case s.MemberExpression:U(b.object,a);b.computed&&U(b.property,a);b.constant=b.object.constant&&(!b.computed||b.property.constant);b.toWatch=[b];break;case s.CallExpression:c=b.filter?!a(b.callee.name).$stateful:!1;d=[];m(b.arguments,function(b){U(b,a);c=c&&b.constant;b.constant||d.push.apply(d,b.toWatch)});b.constant=c;b.toWatch=b.filter&& +!a(b.callee.name).$stateful?d:[b];break;case s.AssignmentExpression:U(b.left,a);U(b.right,a);b.constant=b.left.constant&&b.right.constant;b.toWatch=[b];break;case s.ArrayExpression:c=!0;d=[];m(b.elements,function(b){U(b,a);c=c&&b.constant;b.constant||d.push.apply(d,b.toWatch)});b.constant=c;b.toWatch=d;break;case s.ObjectExpression:c=!0;d=[];m(b.properties,function(b){U(b.value,a);c=c&&b.value.constant;b.value.constant||d.push.apply(d,b.value.toWatch)});b.constant=c;b.toWatch=d;break;case s.ThisExpression:b.constant= +!1,b.toWatch=[]}}function nd(b){if(1==b.length){b=b[0].expression;var a=b.toWatch;return 1!==a.length?a:a[0]!==b?a:t}}function od(b){return b.type===s.Identifier||b.type===s.MemberExpression}function pd(b){if(1===b.body.length&&od(b.body[0].expression))return{type:s.AssignmentExpression,left:b.body[0].expression,right:{type:s.NGValueParameter},operator:"="}}function qd(b){return 0===b.body.length||1===b.body.length&&(b.body[0].expression.type===s.Literal||b.body[0].expression.type===s.ArrayExpression|| +b.body[0].expression.type===s.ObjectExpression)}function rd(b,a){this.astBuilder=b;this.$filter=a}function sd(b,a){this.astBuilder=b;this.$filter=a}function Fb(b){return"constructor"==b}function dc(b){return C(b.valueOf)?b.valueOf():Yf.call(b)}function jf(){var b=ga(),a=ga();this.$get=["$filter",function(c){function d(a,b){return null==a||null==b?a===b:"object"===typeof a&&(a=dc(a),"object"===typeof a)?!1:a===b||a!==a&&b!==b}function e(a,b,c,e,f){var g=e.inputs,h;if(1===g.length){var l=d,g=g[0];return a.$watch(function(a){var b= +g(a);d(b,l)||(h=e(a,t,t,[b]),l=b&&dc(b));return h},b,c,f)}for(var k=[],n=[],q=0,m=g.length;q=this.promise.$$state.status&&d&&d.length&&b(function(){for(var b, +e,f=0,g=d.length;fa)for(b in k++,f)e.hasOwnProperty(b)||(u--,delete f[b])}else f!==e&&(f=e,k++);return k}}c.$stateful=!0;var d=this,e,f,g,l=1u&&(E=4-u,w[E]||(w[E]=[]),w[E].push({msg:C(b.exp)?"fn: "+(b.exp.name||b.exp.toString()):b.exp,newVal:f,oldVal:h}));else if(b===d){r=!1;break a}}catch(y){g(y)}if(!(k=m.$$watchersCount&& +m.$$childHead||m!==this&&m.$$nextSibling))for(;m!==this&&!(k=m.$$nextSibling);)m=m.$parent}while(m=k);if((r||t.length)&&!u--)throw p.$$phase=null,c("infdig",a,w);}while(r||t.length);for(p.$$phase=null;x.length;)try{x.shift()()}catch(z){g(z)}},$destroy:function(){if(!this.$$destroyed){var a=this.$parent;this.$broadcast("$destroy");this.$$destroyed=!0;this===p&&l.$$applicationDestroyed();r(this,-this.$$watchersCount);for(var b in this.$$listenerCount)u(this,this.$$listenerCount[b],b);a&&a.$$childHead== +this&&(a.$$childHead=this.$$nextSibling);a&&a.$$childTail==this&&(a.$$childTail=this.$$prevSibling);this.$$prevSibling&&(this.$$prevSibling.$$nextSibling=this.$$nextSibling);this.$$nextSibling&&(this.$$nextSibling.$$prevSibling=this.$$prevSibling);this.$destroy=this.$digest=this.$apply=this.$evalAsync=this.$applyAsync=v;this.$on=this.$watch=this.$watchGroup=function(){return v};this.$$listeners={};this.$parent=this.$$nextSibling=this.$$prevSibling=this.$$childHead=this.$$childTail=this.$root=this.$$watchers= +null}},$eval:function(a,b){return h(a)(this,b)},$evalAsync:function(a,b){p.$$phase||t.length||l.defer(function(){t.length&&p.$digest()});t.push({scope:this,expression:a,locals:b})},$$postDigest:function(a){x.push(a)},$apply:function(a){try{q("$apply");try{return this.$eval(a)}finally{p.$$phase=null}}catch(b){g(b)}finally{try{p.$digest()}catch(c){throw g(c),c;}}},$applyAsync:function(a){function b(){c.$eval(a)}var c=this;a&&H.push(b);w()},$on:function(a,b){var c=this.$$listeners[a];c||(this.$$listeners[a]= +c=[]);c.push(b);var d=this;do d.$$listenerCount[a]||(d.$$listenerCount[a]=0),d.$$listenerCount[a]++;while(d=d.$parent);var e=this;return function(){var d=c.indexOf(b);-1!==d&&(c[d]=null,u(e,1,a))}},$emit:function(a,b){var c=[],d,e=this,f=!1,h={name:a,targetScope:e,stopPropagation:function(){f=!0},preventDefault:function(){h.defaultPrevented=!0},defaultPrevented:!1},l=cb([h],arguments,1),k,n;do{d=e.$$listeners[a]||c;h.currentScope=e;k=0;for(n=d.length;kVa)throw Ca("iequirks");var d=ia(oa);d.isEnabled=function(){return b};d.trustAs=c.trustAs;d.getTrusted=c.getTrusted;d.valueOf= +c.valueOf;b||(d.trustAs=d.getTrusted=function(a,b){return b},d.valueOf=Za);d.parseAs=function(b,c){var e=a(c);return e.literal&&e.constant?e:a(c,function(a){return d.getTrusted(b,a)})};var e=d.parseAs,f=d.getTrusted,g=d.trustAs;m(oa,function(a,b){var c=M(b);d[fb("parse_as_"+c)]=function(b){return e(a,b)};d[fb("get_trusted_"+c)]=function(b){return f(a,b)};d[fb("trust_as_"+c)]=function(b){return g(a,b)}});return d}]}function pf(){this.$get=["$window","$document",function(b,a){var c={},d=Y((/android (\d+)/.exec(M((b.navigator|| +{}).userAgent))||[])[1]),e=/Boxee/i.test((b.navigator||{}).userAgent),f=a[0]||{},g,h=/^(Moz|webkit|ms)(?=[A-Z])/,l=f.body&&f.body.style,k=!1,n=!1;if(l){for(var q in l)if(k=h.exec(q)){g=k[0];g=g.substr(0,1).toUpperCase()+g.substr(1);break}g||(g="WebkitOpacity"in l&&"webkit");k=!!("transition"in l||g+"Transition"in l);n=!!("animation"in l||g+"Animation"in l);!d||k&&n||(k=I(l.webkitTransition),n=I(l.webkitAnimation))}return{history:!(!b.history||!b.history.pushState||4>d||e),hasEvent:function(a){if("input"=== +a&&11>=Va)return!1;if(y(c[a])){var b=f.createElement("div");c[a]="on"+a in b}return c[a]},csp:Fa(),vendorPrefix:g,transitions:k,animations:n,android:d}}]}function rf(){this.$get=["$templateCache","$http","$q","$sce",function(b,a,c,d){function e(f,g){e.totalPendingRequests++;I(f)&&b.get(f)||(f=d.getTrustedResourceUrl(f));var h=a.defaults&&a.defaults.transformResponse;G(h)?h=h.filter(function(a){return a!==Zb}):h===Zb&&(h=null);return a.get(f,{cache:b,transformResponse:h})["finally"](function(){e.totalPendingRequests--}).then(function(a){b.put(f, +a.data);return a.data},function(a){if(!g)throw ea("tpload",f,a.status,a.statusText);return c.reject(a)})}e.totalPendingRequests=0;return e}]}function sf(){this.$get=["$rootScope","$browser","$location",function(b,a,c){return{findBindings:function(a,b,c){a=a.getElementsByClassName("ng-binding");var g=[];m(a,function(a){var d=aa.element(a).data("$binding");d&&m(d,function(d){c?(new RegExp("(^|\\s)"+ud(b)+"(\\s|\\||$)")).test(d)&&g.push(a):-1!=d.indexOf(b)&&g.push(a)})});return g},findModels:function(a, +b,c){for(var g=["ng-","data-ng-","ng\\:"],h=0;hb;b=Math.abs(b);var g=Infinity===b;if(!g&&!isFinite(b))return"";var h=b+"",l="",k=!1,n=[];g&&(l="\u221e"); +if(!g&&-1!==h.indexOf("e")){var q=h.match(/([\d\.]+)e(-?)(\d+)/);q&&"-"==q[2]&&q[3]>e+1?b=0:(l=h,k=!0)}if(g||k)0b&&(l=b.toFixed(e),b=parseFloat(l));else{g=(h.split(Dd)[1]||"").length;y(e)&&(e=Math.min(Math.max(a.minFrac,g),a.maxFrac));b=+(Math.round(+(b.toString()+"e"+e)).toString()+"e"+-e);var g=(""+b).split(Dd),h=g[0],g=g[1]||"",q=0,r=a.lgSize,u=a.gSize;if(h.length>=r+u)for(q=h.length-r,k=0;kb&&(d="-",b=-b);for(b=""+b;b.length-c)e+=c;0===e&&-12==c&&(e=12);return Gb(e,a,d)}}function Hb(b,a){return function(c,d){var e=c["get"+b](),f=rb(a?"SHORT"+b:b);return d[f][e]}}function Ed(b){var a= +(new Date(b,0,1)).getDay();return new Date(b,0,(4>=a?5:12)-a)}function Fd(b){return function(a){var c=Ed(a.getFullYear());a=+new Date(a.getFullYear(),a.getMonth(),a.getDate()+(4-a.getDay()))-+c;a=1+Math.round(a/6048E5);return Gb(a,b)}}function hc(b,a){return 0>=b.getFullYear()?a.ERAS[0]:a.ERAS[1]}function zd(b){function a(a){var b;if(b=a.match(c)){a=new Date(0);var f=0,g=0,h=b[8]?a.setUTCFullYear:a.setFullYear,l=b[8]?a.setUTCHours:a.setHours;b[9]&&(f=Y(b[9]+b[10]),g=Y(b[9]+b[11]));h.call(a,Y(b[1]), +Y(b[2])-1,Y(b[3]));f=Y(b[4]||0)-f;g=Y(b[5]||0)-g;h=Y(b[6]||0);b=Math.round(1E3*parseFloat("0."+(b[7]||0)));l.call(a,f,g,h,b)}return a}var c=/^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;return function(c,e,f){var g="",h=[],l,k;e=e||"mediumDate";e=b.DATETIME_FORMATS[e]||e;I(c)&&(c=fg.test(c)?Y(c):a(c));X(c)&&(c=new Date(c));if(!ca(c)||!isFinite(c.getTime()))return c;for(;e;)(k=gg.exec(e))?(h=cb(h,k,1),e=h.pop()):(h.push(e),e=null);var n=c.getTimezoneOffset(); +f&&(n=vc(f,c.getTimezoneOffset()),c=Ob(c,f,!0));m(h,function(a){l=hg[a];g+=l?l(c,b.DATETIME_FORMATS,n):a.replace(/(^'|'$)/g,"").replace(/''/g,"'")});return g}}function ag(){return function(b,a){y(a)&&(a=2);return db(b,a)}}function bg(){return function(b,a,c){a=Infinity===Math.abs(Number(a))?Number(a):Y(a);if(isNaN(a))return b;X(b)&&(b=b.toString());if(!G(b)&&!I(b))return b;c=!c||isNaN(c)?0:Y(c);c=0>c&&c>=-b.length?b.length+c:c;return 0<=a?b.slice(c,c+a):0===c?b.slice(a,b.length):b.slice(Math.max(0, +c+a),c)}}function Bd(b){function a(a,c){c=c?-1:1;return a.map(function(a){var d=1,h=Za;if(C(a))h=a;else if(I(a)){if("+"==a.charAt(0)||"-"==a.charAt(0))d="-"==a.charAt(0)?-1:1,a=a.substring(1);if(""!==a&&(h=b(a),h.constant))var l=h(),h=function(a){return a[l]}}return{get:h,descending:d*c}})}function c(a){switch(typeof a){case "number":case "boolean":case "string":return!0;default:return!1}}return function(b,e,f){if(!Da(b))return b;G(e)||(e=[e]);0===e.length&&(e=["+"]);var g=a(e,f);g.push({get:function(){return{}}, +descending:f?-1:1});b=Array.prototype.map.call(b,function(a,b){return{value:a,predicateValues:g.map(function(d){var e=d.get(a);d=typeof e;if(null===e)d="string",e="null";else if("string"===d)e=e.toLowerCase();else if("object"===d)a:{if("function"===typeof e.valueOf&&(e=e.valueOf(),c(e)))break a;if(pc(e)&&(e=e.toString(),c(e)))break a;e=b}return{value:e,type:d}})}});b.sort(function(a,b){for(var c=0,d=0,e=g.length;db||37<=b&&40>=b||n(a,this,this.value)});if(e.hasEvent("paste"))a.on("paste cut",n)}a.on("change",l);d.$render=function(){var b=d.$isEmpty(d.$viewValue)?"":d.$viewValue;a.val()!==b&&a.val(b)}}function Kb(b,a){return function(c,d){var e,f;if(ca(c))return c;if(I(c)){'"'==c.charAt(0)&&'"'==c.charAt(c.length- +1)&&(c=c.substring(1,c.length-1));if(ig.test(c))return new Date(c);b.lastIndex=0;if(e=b.exec(c))return e.shift(),f=d?{yyyy:d.getFullYear(),MM:d.getMonth()+1,dd:d.getDate(),HH:d.getHours(),mm:d.getMinutes(),ss:d.getSeconds(),sss:d.getMilliseconds()/1E3}:{yyyy:1970,MM:1,dd:1,HH:0,mm:0,ss:0,sss:0},m(e,function(b,c){c=s};g.$observe("min",function(a){s= +r(a);h.$validate()})}if(x(g.max)||g.ngMax){var w;h.$validators.max=function(a){return!q(a)||y(w)||c(a)<=w};g.$observe("max",function(a){w=r(a);h.$validate()})}}}function Id(b,a,c,d){(d.$$hasNativeValidators=D(a[0].validity))&&d.$parsers.push(function(b){var c=a.prop("validity")||{};return c.badInput&&!c.typeMismatch?t:b})}function Jd(b,a,c,d,e){if(x(d)){b=b(d);if(!b.constant)throw kb("constexpr",c,d);return b(a)}return e}function jc(b,a){b="ngClass"+b;return["$animate",function(c){function d(a,b){var c= +[],d=0;a:for(;d(?:<\/\1>|)$/,Sb=/<|&#?\w+;/,Bf=/<([\w:]+)/,Cf=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,na={option:[1,'"],thead:[1,"","
"],col:[2,"", +"
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};na.optgroup=na.option;na.tbody=na.tfoot=na.colgroup=na.caption=na.thead;na.th=na.td;var Ra=R.prototype={ready:function(b){function a(){c||(c=!0,b())}var c=!1;"complete"===W.readyState?setTimeout(a):(this.on("DOMContentLoaded",a),R(O).on("load",a))},toString:function(){var b=[];m(this,function(a){b.push(""+a)});return"["+b.join(", ")+"]"},eq:function(b){return 0<= +b?z(this[b]):z(this[this.length+b])},length:0,push:kg,sort:[].sort,splice:[].splice},Ab={};m("multiple selected checked disabled readOnly required open".split(" "),function(b){Ab[M(b)]=b});var Rc={};m("input select option textarea button form details".split(" "),function(b){Rc[b]=!0});var Sc={ngMinlength:"minlength",ngMaxlength:"maxlength",ngMin:"min",ngMax:"max",ngPattern:"pattern"};m({data:Vb,removeData:ub,hasData:function(b){for(var a in gb[b.ng339])return!0;return!1}},function(b,a){R[a]=b});m({data:Vb, +inheritedData:zb,scope:function(b){return z.data(b,"$scope")||zb(b.parentNode||b,["$isolateScope","$scope"])},isolateScope:function(b){return z.data(b,"$isolateScope")||z.data(b,"$isolateScopeNoTemplate")},controller:Oc,injector:function(b){return zb(b,"$injector")},removeAttr:function(b,a){b.removeAttribute(a)},hasClass:wb,css:function(b,a,c){a=fb(a);if(x(c))b.style[a]=c;else return b.style[a]},attr:function(b,a,c){var d=b.nodeType;if(d!==Pa&&2!==d&&8!==d)if(d=M(a),Ab[d])if(x(c))c?(b[a]=!0,b.setAttribute(a, +d)):(b[a]=!1,b.removeAttribute(d));else return b[a]||(b.attributes.getNamedItem(a)||v).specified?d:t;else if(x(c))b.setAttribute(a,c);else if(b.getAttribute)return b=b.getAttribute(a,2),null===b?t:b},prop:function(b,a,c){if(x(c))b[a]=c;else return b[a]},text:function(){function b(a,b){if(y(b)){var d=a.nodeType;return d===pa||d===Pa?a.textContent:""}a.textContent=b}b.$dv="";return b}(),val:function(b,a){if(y(a)){if(b.multiple&&"select"===ta(b)){var c=[];m(b.options,function(a){a.selected&&c.push(a.value|| +a.text)});return 0===c.length?null:c}return b.value}b.value=a},html:function(b,a){if(y(a))return b.innerHTML;tb(b,!0);b.innerHTML=a},empty:Pc},function(b,a){R.prototype[a]=function(a,d){var e,f,g=this.length;if(b!==Pc&&(2==b.length&&b!==wb&&b!==Oc?a:d)===t){if(D(a)){for(e=0;e <= >= && || ! = |".split(" "),function(a){Lb[a]=!0});var rg={n:"\n",f:"\f",r:"\r",t:"\t",v:"\v","'":"'",'"':'"'},ec=function(a){this.options=a};ec.prototype={constructor:ec,lex:function(a){this.text=a;this.index=0;for(this.tokens=[];this.index=a&&"string"===typeof a},isWhitespace:function(a){return" "===a||"\r"===a||"\t"===a||"\n"===a||"\v"===a||"\u00a0"===a},isIdent:function(a){return"a"<=a&&"z">=a||"A"<=a&&"Z">=a||"_"===a||"$"===a},isExpOperator:function(a){return"-"===a||"+"===a||this.isNumber(a)},throwError:function(a,c,d){d=d||this.index;c=x(c)?"s "+c+"-"+ +this.index+" ["+this.text.substring(c,d)+"]":" "+d;throw da("lexerr",a,c,this.text);},readNumber:function(){for(var a="",c=this.index;this.index","<=",">=");)a={type:s.BinaryExpression,operator:c.text,left:a,right:this.additive()};return a},additive:function(){for(var a=this.multiplicative(), +c;c=this.expect("+","-");)a={type:s.BinaryExpression,operator:c.text,left:a,right:this.multiplicative()};return a},multiplicative:function(){for(var a=this.unary(),c;c=this.expect("*","/","%");)a={type:s.BinaryExpression,operator:c.text,left:a,right:this.unary()};return a},unary:function(){var a;return(a=this.expect("+","-","!"))?{type:s.UnaryExpression,operator:a.text,prefix:!0,argument:this.unary()}:this.primary()},primary:function(){var a;this.expect("(")?(a=this.filterChain(),this.consume(")")): +this.expect("[")?a=this.arrayDeclaration():this.expect("{")?a=this.object():this.constants.hasOwnProperty(this.peek().text)?a=fa(this.constants[this.consume().text]):this.peek().identifier?a=this.identifier():this.peek().constant?a=this.constant():this.throwError("not a primary expression",this.peek());for(var c;c=this.expect("(","[",".");)"("===c.text?(a={type:s.CallExpression,callee:a,arguments:this.parseArguments()},this.consume(")")):"["===c.text?(a={type:s.MemberExpression,object:a,property:this.expression(), +computed:!0},this.consume("]")):"."===c.text?a={type:s.MemberExpression,object:a,property:this.identifier(),computed:!1}:this.throwError("IMPOSSIBLE");return a},filter:function(a){a=[a];for(var c={type:s.CallExpression,callee:this.identifier(),arguments:a,filter:!0};this.expect(":");)a.push(this.expression());return c},parseArguments:function(){var a=[];if(")"!==this.peekToken().text){do a.push(this.expression());while(this.expect(","))}return a},identifier:function(){var a=this.consume();a.identifier|| +this.throwError("is not a valid identifier",a);return{type:s.Identifier,name:a.text}},constant:function(){return{type:s.Literal,value:this.consume().value}},arrayDeclaration:function(){var a=[];if("]"!==this.peekToken().text){do{if(this.peek("]"))break;a.push(this.expression())}while(this.expect(","))}this.consume("]");return{type:s.ArrayExpression,elements:a}},object:function(){var a=[],c;if("}"!==this.peekToken().text){do{if(this.peek("}"))break;c={type:s.Property,kind:"init"};this.peek().constant? +c.key=this.constant():this.peek().identifier?c.key=this.identifier():this.throwError("invalid key",this.peek());this.consume(":");c.value=this.expression();a.push(c)}while(this.expect(","))}this.consume("}");return{type:s.ObjectExpression,properties:a}},throwError:function(a,c){throw da("syntax",c.text,a,c.index+1,this.text,this.text.substring(c.index));},consume:function(a){if(0===this.tokens.length)throw da("ueoe",this.text);var c=this.expect(a);c||this.throwError("is unexpected, expecting ["+a+ +"]",this.peek());return c},peekToken:function(){if(0===this.tokens.length)throw da("ueoe",this.text);return this.tokens[0]},peek:function(a,c,d,e){return this.peekAhead(0,a,c,d,e)},peekAhead:function(a,c,d,e,f){if(this.tokens.length>a){a=this.tokens[a];var g=a.text;if(g===c||g===d||g===e||g===f||!(c||d||e||f))return a}return!1},expect:function(a,c,d,e){return(a=this.peek(a,c,d,e))?(this.tokens.shift(),a):!1},constants:{"true":{type:s.Literal,value:!0},"false":{type:s.Literal,value:!1},"null":{type:s.Literal, +value:null},undefined:{type:s.Literal,value:t},"this":{type:s.ThisExpression}}};rd.prototype={compile:function(a,c){var d=this,e=this.astBuilder.ast(a);this.state={nextId:0,filters:{},expensiveChecks:c,fn:{vars:[],body:[],own:{}},assign:{vars:[],body:[],own:{}},inputs:[]};U(e,d.$filter);var f="",g;this.stage="assign";if(g=pd(e))this.state.computing="assign",f=this.nextId(),this.recurse(g,f),f="fn.assign="+this.generateFunction("assign","s,v,l");g=nd(e.body);d.stage="inputs";m(g,function(a,c){var e= +"fn"+c;d.state[e]={vars:[],body:[],own:{}};d.state.computing=e;var f=d.nextId();d.recurse(a,f);d.return_(f);d.state.inputs.push(e);a.watchId=c});this.state.computing="fn";this.stage="main";this.recurse(e);f='"'+this.USE+" "+this.STRICT+'";\n'+this.filterPrefix()+"var fn="+this.generateFunction("fn","s,l,a,i")+f+this.watchFns()+"return fn;";f=(new Function("$filter","ensureSafeMemberName","ensureSafeObject","ensureSafeFunction","ifDefined","plus","text",f))(this.$filter,Wa,Ba,ld,Xf,md,a);this.state= +this.stage=t;f.literal=qd(e);f.constant=e.constant;return f},USE:"use",STRICT:"strict",watchFns:function(){var a=[],c=this.state.inputs,d=this;m(c,function(c){a.push("var "+c+"="+d.generateFunction(c,"s"))});c.length&&a.push("fn.inputs=["+c.join(",")+"];");return a.join("")},generateFunction:function(a,c){return"function("+c+"){"+this.varsPrefix(a)+this.body(a)+"};"},filterPrefix:function(){var a=[],c=this;m(this.state.filters,function(d,e){a.push(d+"=$filter("+c.escape(e)+")")});return a.length? +"var "+a.join(",")+";":""},varsPrefix:function(a){return this.state[a].vars.length?"var "+this.state[a].vars.join(",")+";":""},body:function(a){return this.state[a].body.join("")},recurse:function(a,c,d,e,f,g){var h,l,k=this,n,q;e=e||v;if(!g&&x(a.watchId))c=c||this.nextId(),this.if_("i",this.lazyAssign(c,this.computedMember("i",a.watchId)),this.lazyRecurse(a,c,d,e,f,!0));else switch(a.type){case s.Program:m(a.body,function(c,d){k.recurse(c.expression,t,t,function(a){l=a});d!==a.body.length-1?k.current().body.push(l, +";"):k.return_(l)});break;case s.Literal:q=this.escape(a.value);this.assign(c,q);e(q);break;case s.UnaryExpression:this.recurse(a.argument,t,t,function(a){l=a});q=a.operator+"("+this.ifDefined(l,0)+")";this.assign(c,q);e(q);break;case s.BinaryExpression:this.recurse(a.left,t,t,function(a){h=a});this.recurse(a.right,t,t,function(a){l=a});q="+"===a.operator?this.plus(h,l):"-"===a.operator?this.ifDefined(h,0)+a.operator+this.ifDefined(l,0):"("+h+")"+a.operator+"("+l+")";this.assign(c,q);e(q);break;case s.LogicalExpression:c= +c||this.nextId();k.recurse(a.left,c);k.if_("&&"===a.operator?c:k.not(c),k.lazyRecurse(a.right,c));e(c);break;case s.ConditionalExpression:c=c||this.nextId();k.recurse(a.test,c);k.if_(c,k.lazyRecurse(a.alternate,c),k.lazyRecurse(a.consequent,c));e(c);break;case s.Identifier:c=c||this.nextId();d&&(d.context="inputs"===k.stage?"s":this.assign(this.nextId(),this.getHasOwnProperty("l",a.name)+"?l:s"),d.computed=!1,d.name=a.name);Wa(a.name);k.if_("inputs"===k.stage||k.not(k.getHasOwnProperty("l",a.name)), +function(){k.if_("inputs"===k.stage||"s",function(){f&&1!==f&&k.if_(k.not(k.nonComputedMember("s",a.name)),k.lazyAssign(k.nonComputedMember("s",a.name),"{}"));k.assign(c,k.nonComputedMember("s",a.name))})},c&&k.lazyAssign(c,k.nonComputedMember("l",a.name)));(k.state.expensiveChecks||Fb(a.name))&&k.addEnsureSafeObject(c);e(c);break;case s.MemberExpression:h=d&&(d.context=this.nextId())||this.nextId();c=c||this.nextId();k.recurse(a.object,h,t,function(){k.if_(k.notNull(h),function(){if(a.computed)l= +k.nextId(),k.recurse(a.property,l),k.addEnsureSafeMemberName(l),f&&1!==f&&k.if_(k.not(k.computedMember(h,l)),k.lazyAssign(k.computedMember(h,l),"{}")),q=k.ensureSafeObject(k.computedMember(h,l)),k.assign(c,q),d&&(d.computed=!0,d.name=l);else{Wa(a.property.name);f&&1!==f&&k.if_(k.not(k.nonComputedMember(h,a.property.name)),k.lazyAssign(k.nonComputedMember(h,a.property.name),"{}"));q=k.nonComputedMember(h,a.property.name);if(k.state.expensiveChecks||Fb(a.property.name))q=k.ensureSafeObject(q);k.assign(c, +q);d&&(d.computed=!1,d.name=a.property.name)}},function(){k.assign(c,"undefined")});e(c)},!!f);break;case s.CallExpression:c=c||this.nextId();a.filter?(l=k.filter(a.callee.name),n=[],m(a.arguments,function(a){var c=k.nextId();k.recurse(a,c);n.push(c)}),q=l+"("+n.join(",")+")",k.assign(c,q),e(c)):(l=k.nextId(),h={},n=[],k.recurse(a.callee,l,h,function(){k.if_(k.notNull(l),function(){k.addEnsureSafeFunction(l);m(a.arguments,function(a){k.recurse(a,k.nextId(),t,function(a){n.push(k.ensureSafeObject(a))})}); +h.name?(k.state.expensiveChecks||k.addEnsureSafeObject(h.context),q=k.member(h.context,h.name,h.computed)+"("+n.join(",")+")"):q=l+"("+n.join(",")+")";q=k.ensureSafeObject(q);k.assign(c,q)},function(){k.assign(c,"undefined")});e(c)}));break;case s.AssignmentExpression:l=this.nextId();h={};if(!od(a.left))throw da("lval");this.recurse(a.left,t,h,function(){k.if_(k.notNull(h.context),function(){k.recurse(a.right,l);k.addEnsureSafeObject(k.member(h.context,h.name,h.computed));q=k.member(h.context,h.name, +h.computed)+a.operator+l;k.assign(c,q);e(c||q)})},1);break;case s.ArrayExpression:n=[];m(a.elements,function(a){k.recurse(a,k.nextId(),t,function(a){n.push(a)})});q="["+n.join(",")+"]";this.assign(c,q);e(q);break;case s.ObjectExpression:n=[];m(a.properties,function(a){k.recurse(a.value,k.nextId(),t,function(c){n.push(k.escape(a.key.type===s.Identifier?a.key.name:""+a.key.value)+":"+c)})});q="{"+n.join(",")+"}";this.assign(c,q);e(q);break;case s.ThisExpression:this.assign(c,"s");e("s");break;case s.NGValueParameter:this.assign(c, +"v"),e("v")}},getHasOwnProperty:function(a,c){var d=a+"."+c,e=this.current().own;e.hasOwnProperty(d)||(e[d]=this.nextId(!1,a+"&&("+this.escape(c)+" in "+a+")"));return e[d]},assign:function(a,c){if(a)return this.current().body.push(a,"=",c,";"),a},filter:function(a){this.state.filters.hasOwnProperty(a)||(this.state.filters[a]=this.nextId(!0));return this.state.filters[a]},ifDefined:function(a,c){return"ifDefined("+a+","+this.escape(c)+")"},plus:function(a,c){return"plus("+a+","+c+")"},return_:function(a){this.current().body.push("return ", +a,";")},if_:function(a,c,d){if(!0===a)c();else{var e=this.current().body;e.push("if(",a,"){");c();e.push("}");d&&(e.push("else{"),d(),e.push("}"))}},not:function(a){return"!("+a+")"},notNull:function(a){return a+"!=null"},nonComputedMember:function(a,c){return a+"."+c},computedMember:function(a,c){return a+"["+c+"]"},member:function(a,c,d){return d?this.computedMember(a,c):this.nonComputedMember(a,c)},addEnsureSafeObject:function(a){this.current().body.push(this.ensureSafeObject(a),";")},addEnsureSafeMemberName:function(a){this.current().body.push(this.ensureSafeMemberName(a), +";")},addEnsureSafeFunction:function(a){this.current().body.push(this.ensureSafeFunction(a),";")},ensureSafeObject:function(a){return"ensureSafeObject("+a+",text)"},ensureSafeMemberName:function(a){return"ensureSafeMemberName("+a+",text)"},ensureSafeFunction:function(a){return"ensureSafeFunction("+a+",text)"},lazyRecurse:function(a,c,d,e,f,g){var h=this;return function(){h.recurse(a,c,d,e,f,g)}},lazyAssign:function(a,c){var d=this;return function(){d.assign(a,c)}},stringEscapeRegex:/[^ a-zA-Z0-9]/g, +stringEscapeFn:function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)},escape:function(a){if(I(a))return"'"+a.replace(this.stringEscapeRegex,this.stringEscapeFn)+"'";if(X(a))return a.toString();if(!0===a)return"true";if(!1===a)return"false";if(null===a)return"null";if("undefined"===typeof a)return"undefined";throw da("esc");},nextId:function(a,c){var d="v"+this.state.nextId++;a||this.current().vars.push(d+(c?"="+c:""));return d},current:function(){return this.state[this.state.computing]}}; +sd.prototype={compile:function(a,c){var d=this,e=this.astBuilder.ast(a);this.expression=a;this.expensiveChecks=c;U(e,d.$filter);var f,g;if(f=pd(e))g=this.recurse(f);f=nd(e.body);var h;f&&(h=[],m(f,function(a,c){var e=d.recurse(a);a.input=e;h.push(e);a.watchId=c}));var l=[];m(e.body,function(a){l.push(d.recurse(a.expression))});f=0===e.body.length?function(){}:1===e.body.length?l[0]:function(a,c){var d;m(l,function(e){d=e(a,c)});return d};g&&(f.assign=function(a,c,d){return g(a,d,c)});h&&(f.inputs= +h);f.literal=qd(e);f.constant=e.constant;return f},recurse:function(a,c,d){var e,f,g=this,h;if(a.input)return this.inputs(a.input,a.watchId);switch(a.type){case s.Literal:return this.value(a.value,c);case s.UnaryExpression:return f=this.recurse(a.argument),this["unary"+a.operator](f,c);case s.BinaryExpression:return e=this.recurse(a.left),f=this.recurse(a.right),this["binary"+a.operator](e,f,c);case s.LogicalExpression:return e=this.recurse(a.left),f=this.recurse(a.right),this["binary"+a.operator](e, +f,c);case s.ConditionalExpression:return this["ternary?:"](this.recurse(a.test),this.recurse(a.alternate),this.recurse(a.consequent),c);case s.Identifier:return Wa(a.name,g.expression),g.identifier(a.name,g.expensiveChecks||Fb(a.name),c,d,g.expression);case s.MemberExpression:return e=this.recurse(a.object,!1,!!d),a.computed||(Wa(a.property.name,g.expression),f=a.property.name),a.computed&&(f=this.recurse(a.property)),a.computed?this.computedMember(e,f,c,d,g.expression):this.nonComputedMember(e,f, +g.expensiveChecks,c,d,g.expression);case s.CallExpression:return h=[],m(a.arguments,function(a){h.push(g.recurse(a))}),a.filter&&(f=this.$filter(a.callee.name)),a.filter||(f=this.recurse(a.callee,!0)),a.filter?function(a,d,e,g){for(var r=[],m=0;m":function(a,c,d){return function(e,f,g,h){e=a(e,f,g,h)>c(e,f,g,h);return d?{value:e}:e}},"binary<=":function(a,c,d){return function(e,f,g,h){e=a(e,f,g,h)<=c(e,f,g,h);return d?{value:e}:e}},"binary>=":function(a,c,d){return function(e,f,g,h){e=a(e,f,g,h)>=c(e,f,g,h);return d?{value:e}:e}},"binary&&":function(a,c,d){return function(e,f,g,h){e= +a(e,f,g,h)&&c(e,f,g,h);return d?{value:e}:e}},"binary||":function(a,c,d){return function(e,f,g,h){e=a(e,f,g,h)||c(e,f,g,h);return d?{value:e}:e}},"ternary?:":function(a,c,d,e){return function(f,g,h,l){f=a(f,g,h,l)?c(f,g,h,l):d(f,g,h,l);return e?{value:f}:f}},value:function(a,c){return function(){return c?{context:t,name:t,value:a}:a}},identifier:function(a,c,d,e,f){return function(g,h,l,k){g=h&&a in h?h:g;e&&1!==e&&g&&!g[a]&&(g[a]={});h=g?g[a]:t;c&&Ba(h,f);return d?{context:g,name:a,value:h}:h}}, +computedMember:function(a,c,d,e,f){return function(g,h,l,k){var n=a(g,h,l,k),q,m;null!=n&&(q=c(g,h,l,k),Wa(q,f),e&&1!==e&&n&&!n[q]&&(n[q]={}),m=n[q],Ba(m,f));return d?{context:n,name:q,value:m}:m}},nonComputedMember:function(a,c,d,e,f,g){return function(h,l,k,n){h=a(h,l,k,n);f&&1!==f&&h&&!h[c]&&(h[c]={});l=null!=h?h[c]:t;(d||Fb(c))&&Ba(l,g);return e?{context:h,name:c,value:l}:l}},inputs:function(a,c){return function(d,e,f,g){return g?g[c]:a(d,e,f)}}};var fc=function(a,c,d){this.lexer=a;this.$filter= +c;this.options=d;this.ast=new s(this.lexer);this.astCompiler=d.csp?new sd(this.ast,c):new rd(this.ast,c)};fc.prototype={constructor:fc,parse:function(a){return this.astCompiler.compile(a,this.options.expensiveChecks)}};ga();ga();var Yf=Object.prototype.valueOf,Ca=L("$sce"),oa={HTML:"html",CSS:"css",URL:"url",RESOURCE_URL:"resourceUrl",JS:"js"},ea=L("$compile"),Z=W.createElement("a"),wd=Aa(O.location.href);xd.$inject=["$document"];Jc.$inject=["$provide"];yd.$inject=["$locale"];Ad.$inject=["$locale"]; +var Dd=".",hg={yyyy:$("FullYear",4),yy:$("FullYear",2,0,!0),y:$("FullYear",1),MMMM:Hb("Month"),MMM:Hb("Month",!0),MM:$("Month",2,1),M:$("Month",1,1),dd:$("Date",2),d:$("Date",1),HH:$("Hours",2),H:$("Hours",1),hh:$("Hours",2,-12),h:$("Hours",1,-12),mm:$("Minutes",2),m:$("Minutes",1),ss:$("Seconds",2),s:$("Seconds",1),sss:$("Milliseconds",3),EEEE:Hb("Day"),EEE:Hb("Day",!0),a:function(a,c){return 12>a.getHours()?c.AMPMS[0]:c.AMPMS[1]},Z:function(a,c,d){a=-1*d;return a=(0<=a?"+":"")+(Gb(Math[0=a.getFullYear()?c.ERANAMES[0]:c.ERANAMES[1]}},gg=/((?:[^yMdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,fg=/^\-?\d+$/;zd.$inject=["$locale"];var cg=qa(M),dg=qa(rb);Bd.$inject=["$parse"];var he=qa({restrict:"E",compile:function(a,c){if(!c.href&&!c.xlinkHref)return function(a,c){if("a"===c[0].nodeName.toLowerCase()){var f="[object SVGAnimatedString]"===sa.call(c.prop("href"))? +"xlink:href":"href";c.on("click",function(a){c.attr(f)||a.preventDefault()})}}}}),sb={};m(Ab,function(a,c){function d(a,d,f){a.$watch(f[e],function(a){f.$set(c,!!a)})}if("multiple"!=a){var e=va("ng-"+c),f=d;"checked"===a&&(f=function(a,c,f){f.ngModel!==f[e]&&d(a,c,f)});sb[e]=function(){return{restrict:"A",priority:100,link:f}}}});m(Sc,function(a,c){sb[c]=function(){return{priority:100,link:function(a,e,f){if("ngPattern"===c&&"/"==f.ngPattern.charAt(0)&&(e=f.ngPattern.match(jg))){f.$set("ngPattern", +new RegExp(e[1],e[2]));return}a.$watch(f[c],function(a){f.$set(c,a)})}}}});m(["src","srcset","href"],function(a){var c=va("ng-"+a);sb[c]=function(){return{priority:99,link:function(d,e,f){var g=a,h=a;"href"===a&&"[object SVGAnimatedString]"===sa.call(e.prop("href"))&&(h="xlinkHref",f.$attr[h]="xlink:href",g=null);f.$observe(c,function(c){c?(f.$set(h,c),Va&&g&&e.prop(g,f[h])):"href"===a&&f.$set(h,null)})}}}});var Ib={$addControl:v,$$renameControl:function(a,c){a.$name=c},$removeControl:v,$setValidity:v, +$setDirty:v,$setPristine:v,$setSubmitted:v};Gd.$inject=["$element","$attrs","$scope","$animate","$interpolate"];var Od=function(a){return["$timeout","$parse",function(c,d){function e(a){return""===a?d('this[""]').assign:d(a).assign||v}return{name:"form",restrict:a?"EAC":"E",controller:Gd,compile:function(d,g){d.addClass(Xa).addClass(lb);var h=g.name?"name":a&&g.ngForm?"ngForm":!1;return{pre:function(a,d,f,g){if(!("action"in f)){var m=function(c){a.$apply(function(){g.$commitViewValue();g.$setSubmitted()}); +c.preventDefault()};d[0].addEventListener("submit",m,!1);d.on("$destroy",function(){c(function(){d[0].removeEventListener("submit",m,!1)},0,!1)})}var s=g.$$parentForm,B=h?e(g.$name):v;h&&(B(a,g),f.$observe(h,function(c){g.$name!==c&&(B(a,t),s.$$renameControl(g,c),B=e(g.$name),B(a,g))}));d.on("$destroy",function(){s.$removeControl(g);B(a,t);Q(g,Ib)})}}}}}]},ie=Od(),ve=Od(!0),ig=/\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/,sg=/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/, +tg=/^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i,ug=/^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/,Pd=/^(\d{4})-(\d{2})-(\d{2})$/,Qd=/^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/,kc=/^(\d{4})-W(\d\d)$/,Rd=/^(\d{4})-(\d\d)$/,Sd=/^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/,Td={text:function(a,c,d,e,f,g){ib(a,c,d,e,f,g);ic(e)},date:jb("date",Pd,Kb(Pd,["yyyy","MM","dd"]),"yyyy-MM-dd"),"datetime-local":jb("datetimelocal",Qd,Kb(Qd, +"yyyy MM dd HH mm ss sss".split(" ")),"yyyy-MM-ddTHH:mm:ss.sss"),time:jb("time",Sd,Kb(Sd,["HH","mm","ss","sss"]),"HH:mm:ss.sss"),week:jb("week",kc,function(a,c){if(ca(a))return a;if(I(a)){kc.lastIndex=0;var d=kc.exec(a);if(d){var e=+d[1],f=+d[2],g=d=0,h=0,l=0,k=Ed(e),f=7*(f-1);c&&(d=c.getHours(),g=c.getMinutes(),h=c.getSeconds(),l=c.getMilliseconds());return new Date(e,0,k.getDate()+f,d,g,h,l)}}return NaN},"yyyy-Www"),month:jb("month",Rd,Kb(Rd,["yyyy","MM"]),"yyyy-MM"),number:function(a,c,d,e,f,g){Id(a, +c,d,e);ib(a,c,d,e,f,g);e.$$parserName="number";e.$parsers.push(function(a){return e.$isEmpty(a)?null:ug.test(a)?parseFloat(a):t});e.$formatters.push(function(a){if(!e.$isEmpty(a)){if(!X(a))throw kb("numfmt",a);a=a.toString()}return a});if(x(d.min)||d.ngMin){var h;e.$validators.min=function(a){return e.$isEmpty(a)||y(h)||a>=h};d.$observe("min",function(a){x(a)&&!X(a)&&(a=parseFloat(a,10));h=X(a)&&!isNaN(a)?a:t;e.$validate()})}if(x(d.max)||d.ngMax){var l;e.$validators.max=function(a){return e.$isEmpty(a)|| +y(l)||a<=l};d.$observe("max",function(a){x(a)&&!X(a)&&(a=parseFloat(a,10));l=X(a)&&!isNaN(a)?a:t;e.$validate()})}},url:function(a,c,d,e,f,g){ib(a,c,d,e,f,g);ic(e);e.$$parserName="url";e.$validators.url=function(a,c){var d=a||c;return e.$isEmpty(d)||sg.test(d)}},email:function(a,c,d,e,f,g){ib(a,c,d,e,f,g);ic(e);e.$$parserName="email";e.$validators.email=function(a,c){var d=a||c;return e.$isEmpty(d)||tg.test(d)}},radio:function(a,c,d,e){y(d.name)&&c.attr("name",++mb);c.on("click",function(a){c[0].checked&& +e.$setViewValue(d.value,a&&a.type)});e.$render=function(){c[0].checked=d.value==e.$viewValue};d.$observe("value",e.$render)},checkbox:function(a,c,d,e,f,g,h,l){var k=Jd(l,a,"ngTrueValue",d.ngTrueValue,!0),n=Jd(l,a,"ngFalseValue",d.ngFalseValue,!1);c.on("click",function(a){e.$setViewValue(c[0].checked,a&&a.type)});e.$render=function(){c[0].checked=e.$viewValue};e.$isEmpty=function(a){return!1===a};e.$formatters.push(function(a){return ka(a,k)});e.$parsers.push(function(a){return a?k:n})},hidden:v, +button:v,submit:v,reset:v,file:v},Dc=["$browser","$sniffer","$filter","$parse",function(a,c,d,e){return{restrict:"E",require:["?ngModel"],link:{pre:function(f,g,h,l){l[0]&&(Td[M(h.type)]||Td.text)(f,g,h,l[0],c,a,d,e)}}}}],vg=/^(true|false|\d+)$/,Ne=function(){return{restrict:"A",priority:100,compile:function(a,c){return vg.test(c.ngValue)?function(a,c,f){f.$set("value",a.$eval(f.ngValue))}:function(a,c,f){a.$watch(f.ngValue,function(a){f.$set("value",a)})}}}},ne=["$compile",function(a){return{restrict:"AC", +compile:function(c){a.$$addBindingClass(c);return function(c,e,f){a.$$addBindingInfo(e,f.ngBind);e=e[0];c.$watch(f.ngBind,function(a){e.textContent=a===t?"":a})}}}}],pe=["$interpolate","$compile",function(a,c){return{compile:function(d){c.$$addBindingClass(d);return function(d,f,g){d=a(f.attr(g.$attr.ngBindTemplate));c.$$addBindingInfo(f,d.expressions);f=f[0];g.$observe("ngBindTemplate",function(a){f.textContent=a===t?"":a})}}}}],oe=["$sce","$parse","$compile",function(a,c,d){return{restrict:"A", +compile:function(e,f){var g=c(f.ngBindHtml),h=c(f.ngBindHtml,function(a){return(a||"").toString()});d.$$addBindingClass(e);return function(c,e,f){d.$$addBindingInfo(e,f.ngBindHtml);c.$watch(h,function(){e.html(a.getTrustedHtml(g(c))||"")})}}}}],Me=qa({restrict:"A",require:"ngModel",link:function(a,c,d,e){e.$viewChangeListeners.push(function(){a.$eval(d.ngChange)})}}),qe=jc("",!0),se=jc("Odd",0),re=jc("Even",1),te=Ma({compile:function(a,c){c.$set("ngCloak",t);a.removeClass("ng-cloak")}}),ue=[function(){return{restrict:"A", +scope:!0,controller:"@",priority:500}}],Ic={},wg={blur:!0,focus:!0};m("click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste".split(" "),function(a){var c=va("ng-"+a);Ic[c]=["$parse","$rootScope",function(d,e){return{restrict:"A",compile:function(f,g){var h=d(g[c],null,!0);return function(c,d){d.on(a,function(d){var f=function(){h(c,{$event:d})};wg[a]&&e.$$phase?c.$evalAsync(f):c.$apply(f)})}}}}]});var xe=["$animate", +function(a){return{multiElement:!0,transclude:"element",priority:600,terminal:!0,restrict:"A",$$tlb:!0,link:function(c,d,e,f,g){var h,l,k;c.$watch(e.ngIf,function(c){c?l||g(function(c,f){l=f;c[c.length++]=W.createComment(" end ngIf: "+e.ngIf+" ");h={clone:c};a.enter(c,d.parent(),d)}):(k&&(k.remove(),k=null),l&&(l.$destroy(),l=null),h&&(k=qb(h.clone),a.leave(k).then(function(){k=null}),h=null))})}}}],ye=["$templateRequest","$anchorScroll","$animate",function(a,c,d){return{restrict:"ECA",priority:400, +terminal:!0,transclude:"element",controller:aa.noop,compile:function(e,f){var g=f.ngInclude||f.src,h=f.onload||"",l=f.autoscroll;return function(e,f,m,r,s){var t=0,v,w,p,A=function(){w&&(w.remove(),w=null);v&&(v.$destroy(),v=null);p&&(d.leave(p).then(function(){w=null}),w=p,p=null)};e.$watch(g,function(g){var m=function(){!x(l)||l&&!e.$eval(l)||c()},q=++t;g?(a(g,!0).then(function(a){if(q===t){var c=e.$new();r.template=a;a=s(c,function(a){A();d.enter(a,null,f).then(m)});v=c;p=a;v.$emit("$includeContentLoaded", +g);e.$eval(h)}},function(){q===t&&(A(),e.$emit("$includeContentError",g))}),e.$emit("$includeContentRequested",g)):(A(),r.template=null)})}}}}],Pe=["$compile",function(a){return{restrict:"ECA",priority:-400,require:"ngInclude",link:function(c,d,e,f){/SVG/.test(d[0].toString())?(d.empty(),a(Lc(f.template,W).childNodes)(c,function(a){d.append(a)},{futureParentElement:d})):(d.html(f.template),a(d.contents())(c))}}}],ze=Ma({priority:450,compile:function(){return{pre:function(a,c,d){a.$eval(d.ngInit)}}}}), +Le=function(){return{restrict:"A",priority:100,require:"ngModel",link:function(a,c,d,e){var f=c.attr(d.$attr.ngList)||", ",g="false"!==d.ngTrim,h=g?T(f):f;e.$parsers.push(function(a){if(!y(a)){var c=[];a&&m(a.split(h),function(a){a&&c.push(g?T(a):a)});return c}});e.$formatters.push(function(a){return G(a)?a.join(f):t});e.$isEmpty=function(a){return!a||!a.length}}}},lb="ng-valid",Kd="ng-invalid",Xa="ng-pristine",Jb="ng-dirty",Md="ng-pending",kb=L("ngModel"),xg=["$scope","$exceptionHandler","$attrs", +"$element","$parse","$animate","$timeout","$rootScope","$q","$interpolate",function(a,c,d,e,f,g,h,l,k,n){this.$modelValue=this.$viewValue=Number.NaN;this.$$rawModelValue=t;this.$validators={};this.$asyncValidators={};this.$parsers=[];this.$formatters=[];this.$viewChangeListeners=[];this.$untouched=!0;this.$touched=!1;this.$pristine=!0;this.$dirty=!1;this.$valid=!0;this.$invalid=!1;this.$error={};this.$$success={};this.$pending=t;this.$name=n(d.name||"",!1)(a);var q=f(d.ngModel),r=q.assign,s=q,B=r, +K=null,w,p=this;this.$$setOptions=function(a){if((p.$options=a)&&a.getterSetter){var c=f(d.ngModel+"()"),g=f(d.ngModel+"($$$p)");s=function(a){var d=q(a);C(d)&&(d=c(a));return d};B=function(a,c){C(q(a))?g(a,{$$$p:p.$modelValue}):r(a,p.$modelValue)}}else if(!q.assign)throw kb("nonassign",d.ngModel,ua(e));};this.$render=v;this.$isEmpty=function(a){return y(a)||""===a||null===a||a!==a};var A=e.inheritedData("$formController")||Ib,z=0;Hd({ctrl:this,$element:e,set:function(a,c){a[c]=!0},unset:function(a, +c){delete a[c]},parentForm:A,$animate:g});this.$setPristine=function(){p.$dirty=!1;p.$pristine=!0;g.removeClass(e,Jb);g.addClass(e,Xa)};this.$setDirty=function(){p.$dirty=!0;p.$pristine=!1;g.removeClass(e,Xa);g.addClass(e,Jb);A.$setDirty()};this.$setUntouched=function(){p.$touched=!1;p.$untouched=!0;g.setClass(e,"ng-untouched","ng-touched")};this.$setTouched=function(){p.$touched=!0;p.$untouched=!1;g.setClass(e,"ng-touched","ng-untouched")};this.$rollbackViewValue=function(){h.cancel(K);p.$viewValue= +p.$$lastCommittedViewValue;p.$render()};this.$validate=function(){if(!X(p.$modelValue)||!isNaN(p.$modelValue)){var a=p.$$rawModelValue,c=p.$valid,d=p.$modelValue,e=p.$options&&p.$options.allowInvalid;p.$$runValidators(a,p.$$lastCommittedViewValue,function(f){e||c===f||(p.$modelValue=f?a:t,p.$modelValue!==d&&p.$$writeModelToScope())})}};this.$$runValidators=function(a,c,d){function e(){var d=!0;m(p.$validators,function(e,f){var h=e(a,c);d=d&&h;g(f,h)});return d?!0:(m(p.$asyncValidators,function(a, +c){g(c,null)}),!1)}function f(){var d=[],e=!0;m(p.$asyncValidators,function(f,h){var k=f(a,c);if(!k||!C(k.then))throw kb("$asyncValidators",k);g(h,t);d.push(k.then(function(){g(h,!0)},function(a){e=!1;g(h,!1)}))});d.length?k.all(d).then(function(){h(e)},v):h(!0)}function g(a,c){l===z&&p.$setValidity(a,c)}function h(a){l===z&&d(a)}z++;var l=z;(function(){var a=p.$$parserName||"parse";if(w===t)g(a,null);else return w||(m(p.$validators,function(a,c){g(c,null)}),m(p.$asyncValidators,function(a,c){g(c, +null)})),g(a,w),w;return!0})()?e()?f():h(!1):h(!1)};this.$commitViewValue=function(){var a=p.$viewValue;h.cancel(K);if(p.$$lastCommittedViewValue!==a||""===a&&p.$$hasNativeValidators)p.$$lastCommittedViewValue=a,p.$pristine&&this.$setDirty(),this.$$parseAndValidate()};this.$$parseAndValidate=function(){var c=p.$$lastCommittedViewValue;if(w=y(c)?t:!0)for(var d=0;df||e.$isEmpty(c)||c.length<=f}}}}},Gc=function(){return{restrict:"A",require:"?ngModel",link:function(a,c,d,e){if(e){var f=0;d.$observe("minlength",function(a){f=Y(a)||0;e.$validate()});e.$validators.minlength=function(a, +c){return e.$isEmpty(c)||c.length>=f}}}}};O.angular.bootstrap?console.log("WARNING: Tried to load angular more than once."):(ce(),ee(aa),aa.module("ngLocale",[],["$provide",function(a){function c(a){a+="";var c=a.indexOf(".");return-1==c?0:a.length-c-1}a.value("$locale",{DATETIME_FORMATS:{AMPMS:["AM","PM"],DAY:"Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),ERANAMES:["Before Christ","Anno Domini"],ERAS:["BC","AD"],FIRSTDAYOFWEEK:6,MONTH:"January February March April May June July August September October November December".split(" "), +SHORTDAY:"Sun Mon Tue Wed Thu Fri Sat".split(" "),SHORTMONTH:"Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),WEEKENDRANGE:[5,6],fullDate:"EEEE, MMMM d, y",longDate:"MMMM d, y",medium:"MMM d, y h:mm:ss a",mediumDate:"MMM d, y",mediumTime:"h:mm:ss a","short":"M/d/yy h:mm a",shortDate:"M/d/yy",shortTime:"h:mm a"},NUMBER_FORMATS:{CURRENCY_SYM:"$",DECIMAL_SEP:".",GROUP_SEP:",",PATTERNS:[{gSize:3,lgSize:3,maxFrac:3,minFrac:0,minInt:1,negPre:"-",negSuf:"",posPre:"",posSuf:""},{gSize:3,lgSize:3, +maxFrac:2,minFrac:2,minInt:1,negPre:"-\u00a4",negSuf:"",posPre:"\u00a4",posSuf:""}]},id:"en-us",pluralCat:function(a,e){var f=a|0,g=e;t===g&&(g=Math.min(c(a),3));Math.pow(10,g);return 1==f&&0==g?"one":"other"}})}]),z(W).ready(function(){Zd(W,yc)}))})(window,document);!window.angular.$$csp().noInlineStyle&&window.angular.element(document.head).prepend(''); +//# sourceMappingURL=angular.min.js.map diff --git a/js/app.js b/js/app.js new file mode 100644 index 0000000..00c23fb --- /dev/null +++ b/js/app.js @@ -0,0 +1,276 @@ +var redirectorApp = angular.module('redirectorApp', []) +.config( [ + '$compileProvider', + function( $compileProvider ) + { + $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|chrome-extension|data):/); + } +]); + +//Directive for file upload: +redirectorApp.directive('fileselected', function() { + return { + restrict: 'A', + link: function(scope, element, attr, ctrl) { + element.bind('change', function(e) { + var f = element[0].files[0]; + element[0].value = ''; + scope.$eval(attr.fileselected, {'$file':f}); + }); + } + } +}); + +//Filter for displaying nice names for request types +redirectorApp.filter('requestTypeDisplay', function() { + return function(input) { + return input.map(function(key) { return requestTypes[key]; }).join(', '); + } +}); + + +var storage = chrome.storage.local; //TODO: Change to sync when Firefox supports it... + +var requestTypes = { + main_frame: "Main window (address bar)", + sub_frame: "IFrames", + stylesheet : "Stylesheets", + script : "Scripts", + image : "Images", + object : "Objects (e.g. Flash videos, Java applets)", + xmlhttprequest : "XMLHttpRequests (Ajax)", + other : "Other" +}; + + +redirectorApp.controller('redirectorController', ['$scope', '$timeout', function($s, $timeout) { + + $s.activeRedirect = null; + $s.editIndex = -1; + $s.requestTypes = requestTypes; + + $s.appliesTo = function(key) { + if (!$s.activeRedirect) { + return; + } + return $s.activeRedirect.appliesTo.indexOf(key) != -1; + }; + + $s.toggleApplies = function(key) { + if (!$s.activeRedirect) { + return; + } + var arr = $s.activeRedirect.appliesTo; + + var index = arr.indexOf(key); + if (index == -1) { + arr.push(key); + } else { + arr.splice(index, 1); + } + + var order = 'main_frame,sub_frame,stylesheet,script,image,object,xmlhttprequest,other'; + + arr.sort(function(a,b) { + return order.indexOf(a) - order.indexOf(b); + }); + }; + + + function closeEditForm() { + $s.editIndex = -1; + $s.activeRedirect = null; + $s.showAdvanced = false; + $s.showModal = false; + } + + $s.createNew = function() { + $s.activeRedirect = new Redirect({}).toObject(); + $s.showModal = true; + }; + + $s.saveRedirect = function() { + if ($s.editIndex >= 0) { + $s.redirects[$s.editIndex] = $s.activeRedirect; + } else { + $s.redirects.push(new Redirect($s.activeRedirect).toObject()); + } + closeEditForm(); + saveChanges(); + }; + + $s.cancelEdit = function() { + closeEditForm(); + } + + function saveChanges() { + + var arr = []; + for (var i=0; i < $s.redirects.length; i++) { + var r = $s.redirects[i]; + arr.push(new Redirect(r).toObject()); + } + + storage.set({redirects:arr}, function() { + console.log('Saved redirects'); + }); + updateExportLink(); + } + + function swap(arr, i, n) { + var item = arr[i]; + arr[i] = arr[n]; + arr[n] = item; + } + + $s.moveUp = function(index) { + if (index == 0) { + return; + } + swap($s.redirects, index, index-1); + saveChanges(); + }; + + $s.moveDown = function(index) { + if (index == $s.redirects.length-1) { + return; + } + swap($s.redirects, index, index+1); + saveChanges(); + }; + + $s.confirmDelete = function(index) { + $s.deleting = $s.redirects[index]; + $s.deletingIndex = index; + $s.showModal = true; + } + + $s.cancelDelete = function(index) { + delete $s.deleting; + delete $s.deletingIndex; + $s.showModal = false; + } + + $s.deleteRedirect = function() { + $s.redirects.splice($s.deletingIndex, 1); + delete $s.deleting; + delete $s.deletingIndex; + $s.showModal = false; + saveChanges(); + }; + + $s.editRedirect = function(index) { + $s.activeRedirect = new Redirect($s.redirects[index]).toObject(); + $s.editIndex = index; + $s.showModal = true; + }; + + $s.toggleDisabled = function(redirect) { + redirect.disabled = !redirect.disabled; + saveChanges(); + } + + $s.redirects = []; + storage.get('redirects', function(results) { + if (!results || !results.redirects) { + return; + } + for (var i=0; i < results.redirects.length; i++) { + $s.redirects.push(new Redirect(results.redirects[i]).toObject()); + } + updateExportLink(); + $s.$apply(); + }); + + function msg(message, success) { + $s.message = message; + $s.messageType = success ? 'success' : 'error'; + + var m = message; + + //Remove the message in 5 seconds if it hasn't been changed... + $timeout(function() { + if ($s.message == m) { + $s.message = null; + } + }, 20 * 1000); + } + + /* Import/Export of Redirects */ + $s.importRedirects = function(file) { + if (!file) { + return; + } + var reader = new FileReader(); + + reader.onload = function(e) { + var data; + try { + var data = JSON.parse(reader.result); + } catch(e) { + msg('Failed to parse JSON data, invalid JSON: ' + (e.message||'').substr(0,100)); + return $s.$apply(); + } + + if (!data.redirects) { + msg('Invalid JSON, missing "redirects" property'); + return $s.$apply(); + } + + var imported = 0, existing = 0; + for (var i = 0; i < data.redirects.length; i++) { + var r = new Redirect(data.redirects[i]); + + if ($s.redirects.some(function(i) { return new Redirect(i).equals(r);})) { + existing++; + } else { + $s.redirects.push(r.toObject()); + imported++; + } + } + if (imported == 0 && existing == 0) { + msg('No redirects existed in the file.'); + } + if (imported > 0 && existing == 0) { + msg('Successfully imported ' + imported + ' redirect' + (imported > 1 ? 's.' : '.'), true); + } + if (imported == 0 && existing > 0) { + msg('All redirects in the file already existed and were ignored.'); + } + if (imported > 0 && existing > 0) { + var m = 'Successfully imported ' + imported + ' redirect' + (imported > 1 ? 's' : '') + '. '; + if (existing == 1) { + m += '1 redirect already existed and was ignored.'; + } else { + m += existing + ' redirects already existed and were ignored.'; + } + msg(m, true); + } + + saveChanges(); + $s.$apply(); + }; + try { + reader.readAsText(file, 'utf-8'); + } catch(e) { + msg('Failed to read import file'); + } + } + + function updateExportLink() { + var redirects = $s.redirects.map(function(r) { + return new Redirect(r).toObject(); + }); + + var exportObj = { + createdBy : 'Redirector v' + chrome.app.getDetails().version, + createdAt : new Date(), + redirects : redirects + }; + + var json = JSON.stringify(exportObj, null, 4); + + $s.redirectDownload = 'data:text/plain;charset=utf-8,' + encodeURIComponent(json); + } + +}]); diff --git a/js/background.js b/js/background.js new file mode 100644 index 0000000..b9ef47b --- /dev/null +++ b/js/background.js @@ -0,0 +1,25 @@ + + +function checkForRedirect(details) { + + //We only allow GET request to be redirected, don't want to accidentally redirect + //sensitive POST parameters + if (details.method != 'GET') { + return null; + } + + if (details.url.match(/mbl\.is/)) { + console.log('Redirecting ' + details.url); + return {redirectUrl: 'http://foo.is'}; //this doesn't work + } + return null; //{redirectUrl: 'http://foo.is'}; //this doesn't work +} + + +var filter = {urls:["http://*/*", "https://*/*"]}; + +//TODO: Better browser detection... +var isChrome = !!navigator.userAgent.match(/ Chrome\//); + +var ev = isChrome ? chrome.webRequest.onBeforeRequest : chrome.webRequest.onBeforeSendHeaders; +ev.addListener(checkForRedirect, filter, ["blocking"]); diff --git a/js/redirect.js b/js/redirect.js new file mode 100644 index 0000000..41e3c1d --- /dev/null +++ b/js/redirect.js @@ -0,0 +1,187 @@ + +function Redirect(o) { + this._init(o); +} + +//Static +Redirect.WILDCARD = 'W'; +Redirect.REGEX = 'R'; + +Redirect.prototype = { + + //attributes + description : '', + exampleUrl : '', + includePattern : '', + excludePattern : '', + redirectUrl : '', + patternType : '', + unescapeMatches : false, + escapeMatches : false, + disabled : false, + + compile : function() { + this._rxInclude = this._compile(this._includePattern); + this._rxExclude = this._compile(this._excludePattern); + }, + + equals : function(redirect) { + return this.description == redirect.description + && this.exampleUrl == redirect.exampleUrl + && this.includePattern == redirect.includePattern + && this.excludePattern == redirect.excludePattern + && this.redirectUrl == redirect.redirectUrl + && this.patternType == redirect.patternType + && this.unescapeMatches == redirect.unescapeMatches + && this.escapeMatches == redirect.escapeMatches + && this.appliesTo.toString() == redirect.appliesTo.toString(); + }, + + toObject : function() { + return { + description : this.description, + exampleUrl : this.exampleUrl, + includePattern : this.includePattern, + excludePattern : this.excludePattern, + redirectUrl : this.redirectUrl, + patternType : this.patternType, + unescapeMatches : this.unescapeMatches, + escapeMatches : this.escapeMatches, + disabled : this.disabled, + appliesTo : this.appliesTo.slice(0) + }; + }, + + getMatch: function(url) { + var result = { + isMatch : false, + isExcludeMatch : false, + isDisabledMatch : false, + redirectTo : '', + toString : function() { return JSON.stringify(this); } + }; + var redirectTo = null; + + redirectTo = this._includeMatch(url); + if (redirectTo !== null) { + if (this.disabled) { + result.isDisabledMatch = true; + } else if (this._excludeMatch(url)) { + result.isExcludeMatch = true; + } else { + result.isMatch = true; + result.redirectTo = redirectTo; + } + } + return result; + }, + + isRegex: function() { + return this.patternType == Redirect.REGEX; + }, + + isWildcard : function() { + return this.patternType == Redirect.WILDCARD; + }, + + test : function() { + return this.getMatch(this.exampleUrl); + }, + + //Private functions below + + _includePattern : null, + _excludePattern : null, + _patternType : null, + _rxInclude : null, + _rxExclude : null, + + _preparePattern : function(pattern) { + if (this.patternType == Redirect.REGEX) { + return pattern; + } else { //Convert wildcard to regex pattern + var converted = '^'; + for (var i = 0; i < pattern.length; i++) { + var ch = pattern.charAt(i); + if ('()[]{}?.^$\\+'.indexOf(ch) != -1) { + converted += '\\' + ch; + } else if (ch == '*') { + converted += '(.*?)'; + } else { + converted += ch; + } + } + converted += '$'; + return converted; + } + }, + + _compile : function(pattern) { + if (!pattern) { + return null; + } + return new RegExp(this._preparePattern(pattern),"gi"); + }, + + _init : function(o) { + this.description = o.description || '', + this.exampleUrl = o.exampleUrl || ''; + this.includePattern = o.includePattern || ''; + this.excludePattern = o.excludePattern || ''; + this.redirectUrl = o.redirectUrl || ''; + this.patternType = o.patternType || Redirect.WILDCARD; + this.unescapeMatches = !!o.unescapeMatches; + this.escapeMatches = !!o.escapeMatches; + this.disabled = !!o.disabled; + if (o.appliesTo && o.appliesTo.length) { + this.appliesTo = o.appliesTo.slice(0); + } else { + this.appliesTo = ['main_frame']; + } + }, + + toString : function() { + return 'REDIRECT: {' + + '\n\tExample url : ' + this.exampleUrl + + '\n\tInclude pattern : ' + this.includePattern + + '\n\tExclude pattern : ' + this.excludePattern + + '\n\tRedirect url : ' + this.redirectUrl + + '\n\tPattern type : ' + this.patternType + + '\n\tUnescape matches : ' + this.unescapeMatches + + '\n\tEscape matches : ' + this.escapeMatches + + '\n\tDisabled : ' + this.disabled + + '\n}\n'; + }, + + _includeMatch : function(url) { + if (!this._rxInclude) { + return null; + } + var matches = this._rxInclude.exec(url); + if (!matches) { + return null; + } + var resultUrl = this.redirectUrl; + for (var i = 1; i < matches.length; i++) { + var repl = matches[i] || ''; + if (this.unescapeMatches) { + repl = unescape(repl); + } + if (this.escapeMatches) { + repl = encodeURIComponent(repl); + } + resultUrl = resultUrl.replace(new RegExp('\\$' + i, 'gi'), repl); + } + this._rxInclude.lastIndex = 0; + return resultUrl; + }, + + _excludeMatch : function(url) { + if (!this._rxExclude) { + return false; + } + var shouldExclude = !!this._rxExclude.exec(url); + this._rxExclude.lastIndex = 0; + return shouldExclude; + } +}; diff --git a/js/redirector.js b/js/redirector.js new file mode 100644 index 0000000..86e07a2 --- /dev/null +++ b/js/redirector.js @@ -0,0 +1,342 @@ +Components.utils.import("chrome://redirector/content/js/xpcom.js"); +Components.utils.import("chrome://redirector/content/js/redirect.js"); +Components.utils.import("chrome://redirector/content/js/redirectorprefs.js"); + +var EXPORTED_SYMBOLS = ['Redirector']; + +Redirector = { + + get enabled() { + return this._prefs && this._prefs.enabled; + }, + + set enabled(value) { + if (this._prefs) { + this._prefs.enabled = value; + } + }, + + get redirectCount() { + return this._list.length; + }, + + toString : function() { + return "Redirector"; + }, + + addRedirect : function(redirect) { + this._list.push(redirect); + this.save(); + }, + + debug : function(msg) { + if (this._prefs.debugEnabled) { + ConsoleService.logStringMessage('REDIRECTOR: ' + msg); + } + }, + + deleteRedirect : function(redirect) { + this._list.splice(this._list.indexOf(redirect), 1); + this.save(); + }, + + exportRedirects : function(file) { + const PR_WRONLY = 0x02; + const PR_CREATE_FILE = 0x08; + const PR_TRUNCATE = 0x20; + + var fileStream = new FileOutputStream(file, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0644, 0); + var stream = new ConverterOutputStream(fileStream, "UTF-8", 16384, Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER); + var rjson = { createdBy : 'Redirector v' + this._prefs.version, createdAt : new Date(), redirects :[]}; + for each (var re in this._list) { + rjson.redirects.push(re.toObject()); + } + stream.writeString(JSON.stringify(rjson, null, 4)); + stream.close(); + }, + + getRedirectAt : function(index) { + return this._list[index]; + }, + + //Get the redirect url for the given url. This will not check if we are enabled, and + //not do any verification on the url, just assume that it is a good string url that is for http/s + getRedirectUrl : function(url) { + this.debug("Checking " + url); + + for each (var redirect in this._list) { + var result = redirect.getMatch(url); + if (result.isExcludeMatch) { + this.debug(url + ' matched exclude pattern ' + redirect.excludePattern + ' so the redirect ' + redirect.includePattern + ' will not be used'); + } else if (result.isDisabledMatch) { + this.debug(url + ' matched pattern ' + redirect.includePattern + ' but the redirect is disabled'); + } else if (result.isMatch) { + redirectUrl = this._makeAbsoluteUrl(url, result.redirectTo); + + //check for loops... + result = redirect.getMatch(redirectUrl); + if (result.isMatch) { + var msg = this._getFormattedString('invalidRedirectText', [redirect.includePattern, url, redirectUrl]); + this.debug(msg); + redirect.disabled = true; + this.save(); + ConsoleService.logStringMessage('Redirector: ' + msg); + } else { + this.debug('Redirecting ' + url + ' to ' + redirectUrl); + return redirectUrl; + } + } + } + return null; + }, + + QueryInterface: function(iid) { + if (iid.equals(Ci.nsISupports) || iid.equals(Ci.nsIContentPolicy) || iid.equals(Ci.nsIChannelEventSink)) { + return this; + } + throw Cr.NS_ERROR_NO_INTERFACE; + }, + + _getRedirectsFile : function() { + var file = DirectoryService.get("ProfD", Ci.nsIFile); + file.append('redirector.rjson'); + return file; + }, + + handleUpgrades : function(){ + var currentVersion = '2.7.1'; + this._list = []; + + if (this._prefs.version == currentVersion) { + return; + } + //Here update checks are handled + + try { + var branch = PrefService.getBranch("extensions.redirector."); + var data = branch.getCharPref("redirects"); + } catch(e) { + this._prefs.version = currentVersion; + return; + } + var arr; + this._list = []; + if (data != '') { + for each (redirectString in data.split(':::')) { + if (!redirectString || !redirectString.split) { + continue; + this.debug('Invalid old redirect: ' + redirectString); + } + var parts = redirectString.split(',,,'); + if (parts.length < 5) { + throw Error("Invalid serialized redirect, too few fields: " + redirectString); + } + var redirect = new Redirect(); + redirect._init.apply(redirect, parts); + this._list.push(redirect); + } + this.save(); + this._list = []; //Let the real loading start this properly + } + branch.deleteBranch('redirects'); + this._prefs.version = currentVersion; + }, + + importRedirects : function(file) { + var fileStream = new FileInputStream(file, 0x01, 0444, 0); + var stream = new ConverterInputStream(fileStream, "UTF-8", 16384, Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER); + var str = {}; + var rjson = ''; + while (stream.readString(4096, str) != 0) { + rjson += str.value; + } + stream.close(); + var importCount = 0, existsCount = 0; + rjson = JSON.parse(rjson); + for each (var rd in rjson.redirects) { + var redirect = new Redirect(); + redirect.fromObject(rd); + if (this._containsRedirect(redirect)) { + existsCount++; + } else { + this._list.push(redirect); + importCount++; + } + } + this.save(); + return importCount | (existsCount << 16); + }, + + save : function() { + var file = this._getRedirectsFile(); + this.exportRedirects(file); + }, + + sortRedirects : function(sortFunc) { + this._list.sort(sortFunc); + this.save(); + }, + + // nsIContentPolicy implementation + shouldLoad: function(contentType, contentLocation, requestOrigin, aContext, mimeTypeGuess, extra) { + if (contentLocation.scheme != "http" && contentLocation.scheme != "https") { + return Ci.nsIContentPolicy.ACCEPT; + } //Immediately, otherwise we will log all sorts of crap + + this.debug('nsIContentPolicy::ShouldLoad ' + contentLocation.spec); + try { + //This is also done in getRedirectUrl, but we want to exit as quickly as possible for performance + if (!this._prefs.enabled) { + return Ci.nsIContentPolicy.ACCEPT; + } + + if (contentType != Ci.nsIContentPolicy.TYPE_DOCUMENT) { + return Ci.nsIContentPolicy.ACCEPT; + } + + if (contentLocation.scheme != "http" && contentLocation.scheme != "https") { + return Ci.nsIContentPolicy.ACCEPT; + } + + if (!aContext || !aContext.loadURI) { + return Ci.nsIContentPolicy.ACCEPT; + } + + var redirectUrl = this.getRedirectUrl(contentLocation.spec); + + if (!redirectUrl) { + return Ci.nsIContentPolicy.ACCEPT; + } + + aContext.loadURI(redirectUrl, requestOrigin, null); + return Ci.nsIContentPolicy.REJECT_REQUEST; + } catch(e) { + this.debug(e); + } + + }, + + shouldProcess: function(contentType, contentLocation, requestOrigin, insecNode, mimeType, extra) { + return Ci.nsIContentPolicy.ACCEPT; + }, + //end nsIContentPolicy + + //nsIChannelEventSink implementation + + //For FF4.0. Got this from a thread about adblock plus, https://adblockplus.org/forum/viewtopic.php?t=5895 + asyncOnChannelRedirect: function(oldChannel, newChannel, flags, redirectCallback) { + this.onChannelRedirect(oldChannel, newChannel, flags); + redirectCallback.onRedirectVerifyCallback(0); + }, + + onChannelRedirect: function(oldChannel, newChannel, flags) + { + try { + let newLocation = newChannel.URI.spec; + this.debug('nsIChannelEventSink::onChannelRedirect ' + newLocation); + + if (!(newChannel.loadFlags & Ci.nsIChannel.LOAD_DOCUMENT_URI)) { + //We only redirect documents... + return; + } + + if (!this._prefs.enabled) { + return; + } + + if (!newLocation) { + return; + } + let callbacks = []; + if (newChannel.notificationCallbacks) { + callbacks.push(newChannel.notificationCallbacks); + } + if (newChannel.loadGroup && newChannel.loadGroup.notificationCallbacks) { + callbacks.push(newChannel.loadGroup.notificationCallbacks); + } + var win; + var webNav; + for each (let callback in callbacks) + { + try { + win = callback.getInterface(Ci.nsILoadContext).associatedWindow; + webNav = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation); + break; + } catch(e) {} + } + if (!webNav) { + return; + } + var redirectUrl = this.getRedirectUrl(newLocation); + + if (redirectUrl) { + webNav.loadURI(redirectUrl,null,null,null,null); + throw Cr.NS_BASE_STREAM_WOULD_BLOCK; //Throw this because the real error we should throw shows up in console... + } + + } catch (e if (e != Cr.NS_BASE_STREAM_WOULD_BLOCK)) { + // We shouldn't throw exceptions here - this will prevent the redirect. + this.debug("Redirector: Unexpected error in onChannelRedirect: " + e + "\n"); + } + }, + //end nsIChannelEventSink + + //Private members and methods + + _prefs : null, + _list : null, + _strings : null, + + init : function() { + if (this._prefs) { + this._prefs.dispose(); + } + this._prefs = new RedirectorPrefs(); + this.debug('REDIRECTOR CREATED'); + //Check if we need to update existing redirects + var data = this._prefs.redirects; + var version = this._prefs.version; + this._loadStrings(); + this._list = []; + this.handleUpgrades(); + var redirectsFile = this._getRedirectsFile(); + if (redirectsFile.exists()) { + this.importRedirects(redirectsFile); + } + }, + + _loadStrings : function() { + var src = 'chrome://redirector/locale/redirector.properties'; + var appLocale = LocaleService.getApplicationLocale(); + this._strings = StringBundleService.createBundle(src, appLocale); + }, + + _containsRedirect : function(redirect) { + for each (var existing in this._list) { + if (existing.equals(redirect)) { + return true; + } + } + return false; + }, + + _getString : function(name) { + return this._strings.GetStringFromName(name); + }, + + _getFormattedString : function(name, params) { + return this._strings.formatStringFromName(name, params, params.length); + }, + + _makeAbsoluteUrl : function(currentUrl, relativeUrl) { + + if (relativeUrl.match(/https?:/)) { + return relativeUrl; + } + + var uri = IOService.newURI(currentUrl, null, null); + return uri.resolve(relativeUrl); + } +}; + +Redirector.init(); diff --git a/locale/en-US/browserOverlay.dtd b/locale/en-US/browserOverlay.dtd deleted file mode 100644 index 3aaa0fc..0000000 --- a/locale/en-US/browserOverlay.dtd +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/locale/en-US/editRedirect.xul.dtd b/locale/en-US/editRedirect.xul.dtd deleted file mode 100644 index 4b48d9c..0000000 --- a/locale/en-US/editRedirect.xul.dtd +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/locale/en-US/redirector.properties b/locale/en-US/redirector.properties deleted file mode 100644 index 9203928..0000000 --- a/locale/en-US/redirector.properties +++ /dev/null @@ -1,31 +0,0 @@ -initError=Failed to initialize Redirector. -extensionName=Redirector -addCurrentUrl=Add current URL to Redirector -addLinkUrl=Add link URL to Redirector -recursiveError=A redirect with the pattern\n %S\nmatches\n %S\nand is trying to redirect to it again.\n\nEdit this redirect so it will no longer be recursive. -enabledTooltip=Redirector enabled -disabledTooltip=Redirector disabled -testPatternSuccess=SUCCESS!\n\nThe pattern\n %S\nmatches the example URL\n %S\nand would redirect you to\n %S\n\n -testPatternFailure=FAIL!\n\nThe pattern\n %S\nDOES NOT match the example URL\n %S\n\n -testPatternExclude=The example URL\n %S\nmatches the exclude pattern\n %S\nand so would not be redirected -regexPatternErrorTitle=Invalid regular expression -regexPatternError=The pattern\n %S\nis not a legal regular expression.\n\nDetails:\n%S -xpathDeprecated=XPath patterns are no longer supported as of Redirector 1.5.1.\n\nPlease remove those redirects. -redirectorFiles=Redirector files (*.rjson) -exportCaption=Export redirects... -importCaption=Import redirects... -deleteConfirmationText=Permanently delete this redirect? -deleteConfirmationTitle=Delete redirect? -importedMessage=%S redirects were imported. -importedMessageSingular=%S redirect was imported. -existedMessage=%S redirects were identical to existing redirects and therefore were not imported. -existedMessageSingular=%S redirect was identical to an existing redirect and therefore was not imported. -allExistedMessage=All %S redirects in the file were identical to existing redirects, and therefore no redirects were imported. -allExistedMessageSingular=The single redirect in the file was identical to an existing redirect, and therefore was not imported. -importedNone=There were no usable redirects in the given file, therefore no redirects were imported. -importResult=Import results -invalidRedirectText=The pattern\n %Sredirected the URL\n %S\nto\n %S\nwhich also matches the pattern.\n\nSince this causes an endless loop, the redirect has been disabled.\n\nEdit this redirect to fix the recursion. -warningExampleUrlDoesntMatchPatternTitle=Warning: The example URL does not match the pattern -warningExampleUrlDoesntMatchPattern=The example URL does not match the pattern specified.\n\nSave this redirect?\n -errorExampleUrlMatchesRecursiveTitle=Error: Recursive match detected -errorExampleUrlMatchesRecursive=The example URL\n %S\nmatches the pattern and would redirect to\n %S\nwhich also matches the pattern.\n\nThis is not allowed as it causes endless recursion. diff --git a/locale/en-US/settings.xul.dtd b/locale/en-US/settings.xul.dtd deleted file mode 100644 index 90d3f6f..0000000 --- a/locale/en-US/settings.xul.dtd +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..06ca42a --- /dev/null +++ b/manifest.json @@ -0,0 +1,18 @@ +{ + + "manifest_version": 2, + "name": "Redirector", + "version": "3.0", + + "permissions" : ["webRequest", "webRequestBlocking", "storage", "http://*/*", "https://*/*"], + "applications": { + "gecko": { + "id": "redirector@einaregilsson.com" + } + }, + + "background": { + "scripts": ["js/background.js"], + "persistent": true + } +} diff --git a/redirector.html b/redirector.html new file mode 100644 index 0000000..aa0ccbc --- /dev/null +++ b/redirector.html @@ -0,0 +1,148 @@ + + + + Redirector + + + + + + + +
+
+
+

Are you sure you want to delete this redirect?

+
+ + {{deleting.description}} +
+
+ + {{deleting.exampleUrl}} +
+
+ + {{deleting.includePattern}} +
+
+ + {{deleting.redirectUrl}} +
+
+ + {{deleting.patternType == 'W' ? 'Wildcard' : 'Regular Expression'}} +
+ +
+
+

{{editIndex >= 0 ? 'Edit' : 'Create'}} Redirect

+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + + +
+
+ +
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + + + +
+
+
+ Save + Cancel +
+
+ + +
+

Redirector

+ +
Go where YOU want!
+ + + + +
+ {{message}} + +
+
+
+

[Disabled] {{r.description}}

+
+ {{r.includePattern}} +
+
+ foo.is +
+
+ asdfasdf +
+
+ +
+
+ {{r.appliesTo|requestTypeDisplay}} +
+ +
+
+ +
+ + diff --git a/unittest/run.html b/unittest/run.html new file mode 100644 index 0000000..1c056d5 --- /dev/null +++ b/unittest/run.html @@ -0,0 +1,98 @@ + + + + Redirector Unit Tests + + + + + +

Redirector Unit Tests

+ + +
+
+ + \ No newline at end of file diff --git a/unittest/testcases.js b/unittest/testcases.js new file mode 100644 index 0000000..0c0c10c --- /dev/null +++ b/unittest/testcases.js @@ -0,0 +1,129 @@ +//// $Id$ +var nsIContentPolicy = Components.interfaces.nsIContentPolicy; + +var tests = { + "Wildcard matches" : { + run : function(data,log) { + var pattern = data[0], + url = data[1], + expected = data[2]; + var parts = expected.split(','); + var redirectUrl = ''; + if (!(parts.length == 1 && parts[0] == '')) { + for (var i in parts) { + redirectUrl += '$' + (parseFloat(i)+1) + ','; + } + redirectUrl = redirectUrl.substr(0, redirectUrl.length-1); + } + var redirect = new Redirect(null, pattern, redirectUrl, Redirect.WILDCARD); + var result = redirect.getMatch(url); + return { passed: result.isMatch && (result.redirectTo == expected), message : "Expected '" + expected + "', actual was '" + result.redirectTo + "'"}; + }, + + describe : function(data) { return data[0] + ' == ' + data[1] + ', matches=' + data[2]; }, + tests : [ + ['http://foo*', 'http://foobar.is', 'bar.is'], + ['http://foo*', 'http://foo', ''], + ['*://foo.is', 'http://foo.is', 'http'], + ['*http://foo.is', 'http://foo.is', ''], + ['http*foo*', 'http://foobar.is', '://,bar.is'], + ['http*foo*', 'http://foo', '://,'], + ['*://f*.is', 'http://foo.is', 'http,oo'], + ['*http://f*.is', 'http://foo.is', ',oo'], + ['*foo*', 'http://foo', 'http://,'], + ['*foo*', 'foobar.is', ',bar.is'], + ['*foo*', 'http://foobar.is', 'http://,bar.is'], + ['http://foo.is', 'http://foo.is', ''], + ['*', 'http://foo.is', 'http://foo.is'], + ['*://*oo*bar*', 'http://foo.is/bar/baz', 'http,f,.is/,/baz'], + ['*://**oo*bar*', 'http://foo.is/bar/baz', 'http,,f,.is/,/baz'], + ] + }, + + "Regex matches" : { + run : function(data) { + var pattern = data[0], + url = data[1], + expected = data[2]; + var parts = expected.split(','); + var redirectUrl = ''; + if (!(parts.length == 1 && parts[0] == '')) { + for (var i in parts) { + redirectUrl += '$' + (parseFloat(i)+1) + ','; + } + redirectUrl = redirectUrl.substr(0, redirectUrl.length-1); + } + var redirect = new Redirect(null, pattern, redirectUrl, Redirect.REGEX, null, null); + var result = redirect.getMatch(url); + return { passed: result.isMatch && result.redirectTo == expected, message : "Expected '" + expected + "', actual was '" + result.redirectTo + "'"}; + }, + + describe : function(data) { return data[0] + ' == ' + data[1] + ', matches=' + data[2]; }, + tests : [ + ['http://foo(.*)', 'http://foobar.is', 'bar.is'], + ['http://foo(.*)', 'http://foo', ''], + ['(.*)://foo.is', 'http://foo.is', 'http'], + ['(.*)http://foo\\.is', 'http://foo.is', ''], + ['http(.*)foo(.*)', 'http://foobar.is', '://,bar.is'], + ['http(.*)foo(.*)', 'http://foo', '://,'], + ['(.*)://f(.*)\\.is', 'http://foo.is', 'http,oo'], + ['(.*)http://f(.*)\\.is', 'http://foo.is', ',oo'], + ['(.*)foo(.*)', 'http://foo', 'http://,'], + ['(.*)foo(.*)', 'foobar.is', ',bar.is'], + ['(.*)foo(.*)', 'http://foobar.is', 'http://,bar.is'], + ['http://foo\.is', 'http://foo.is', ''], + ['(.*)', 'http://foo.is', 'http://foo.is'], + ['(.*)://(.*)oo(.*)bar(.*)', 'http://foo.is/bar/baz', 'http,f,.is/,/baz'], + ['(.*)://(.*?)(.*)oo(.*)bar(.*)', 'http://foo.is/bar/baz', 'http,,f,.is/,/baz'], + ] + }, + + "nsIContentPolicy implementation" : { + run : function(data) { + var runTest = function() { + var args = { + contentType : nsIContentPolicy.TYPE_DOCUMENT, + contentLocation : "http://foo.is", + requestOrigin : null, + aContext : { loadURI : function(){}}, + mimeTypeGuess : null, + extra : null + }; + for (var key in data[1]) { + args[key] = data[1][key]; + } + + var ioService = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService); + args.contentLocation = ioService.newURI(args.contentLocation, null, null); + var contentPolicy = redirector.QueryInterface(nsIContentPolicy); + var result = contentPolicy.shouldLoad(args.contentType, args.contentLocation, args.requestOrigin, args.aContext, args.mimeTypeGuess, args.extra); + return { passed: result == nsIContentPolicy.ACCEPT, message : "Expected nsIContentPolicy.ACCEPT, actual was " + result }; + } + + if (typeof data[2] == "function") { + return data[2](runTest); + } else { + return runTest(); + } + }, + + describe : function(data) { return data[0]; }, + tests : [ + ["Accepts if not TYPE_DOCUMENT", { contentType : nsIContentPolicy.TYPE_STYLESHEET}], + ["Accepts if not http or https", { contentLocation : "resource://foo/bar"}], + ["Accepts if no aContext", { aContext : null}], + ["Accepts if aContext has no loadURI function", { aContext : { foo : function(){}}}], + ["Accepts if Redirector is not enabled", {}, function(doFunc) { + try { + redirector.enabled = false; + return doFunc(); + redirector.enabled = true; + + } catch(e) { + redirector.enabled = true; + throw e; + } + }] + ] + } +}; -- cgit v1.2.3