From cd5919dd5f6f67da87b753f210d0c88b7189b41a Mon Sep 17 00:00:00 2001 From: Naoya Matayoshi Date: Fri, 2 Dec 2016 22:49:57 +0800 Subject: [PATCH 1/3] Change paragraph widget to custom widget to display coderdojo index page --- app/models/custom_widget.rb | 5 +++++ app/models/paragraph_widget.rb | 6 ------ .../{paragraph_widget => custom_widget}/details.html.erb | 2 +- app/views/custom_widget/show.html.erb | 0 app/views/custom_widget/thumbnail.html.erb | 1 + app/views/paragraph_widget/show.html.erb | 7 ------- app/views/paragraph_widget/thumbnail.html.erb | 1 - 7 files changed, 7 insertions(+), 15 deletions(-) create mode 100644 app/models/custom_widget.rb delete mode 100644 app/models/paragraph_widget.rb rename app/views/{paragraph_widget => custom_widget}/details.html.erb (56%) create mode 100644 app/views/custom_widget/show.html.erb create mode 100644 app/views/custom_widget/thumbnail.html.erb delete mode 100644 app/views/paragraph_widget/show.html.erb delete mode 100644 app/views/paragraph_widget/thumbnail.html.erb diff --git a/app/models/custom_widget.rb b/app/models/custom_widget.rb new file mode 100644 index 000000000..b4698bb5d --- /dev/null +++ b/app/models/custom_widget.rb @@ -0,0 +1,5 @@ +class CustomWidget < Widget + attribute :image, :reference + attribute :headline, :string + attribute :text, :string +end diff --git a/app/models/paragraph_widget.rb b/app/models/paragraph_widget.rb deleted file mode 100644 index d2075638e..000000000 --- a/app/models/paragraph_widget.rb +++ /dev/null @@ -1,6 +0,0 @@ -class ParagraphWidget < Widget - attribute :image, :reference - attribute :headline, :string - attribute :text, :string - attribute :color, :enum, values: %w[red green blue], default: 'blue' -end diff --git a/app/views/paragraph_widget/details.html.erb b/app/views/custom_widget/details.html.erb similarity index 56% rename from app/views/paragraph_widget/details.html.erb rename to app/views/custom_widget/details.html.erb index a63407642..7c9f3fc40 100644 --- a/app/views/paragraph_widget/details.html.erb +++ b/app/views/custom_widget/details.html.erb @@ -1,5 +1,5 @@ <%= scrivito_medium_dialog do %> - <%= scrivito_details_for ParagraphWidget.description_for_editor do %> + <%= scrivito_details_for CustomWidget.description_for_editor do %> A cover with head, text and image <% end %> <% end %> diff --git a/app/views/custom_widget/show.html.erb b/app/views/custom_widget/show.html.erb new file mode 100644 index 000000000..e69de29bb diff --git a/app/views/custom_widget/thumbnail.html.erb b/app/views/custom_widget/thumbnail.html.erb new file mode 100644 index 000000000..311b663bb --- /dev/null +++ b/app/views/custom_widget/thumbnail.html.erb @@ -0,0 +1 @@ +<%= scrivito_thumbnail CustomWidget.description_for_editor %> diff --git a/app/views/paragraph_widget/show.html.erb b/app/views/paragraph_widget/show.html.erb deleted file mode 100644 index bbf768762..000000000 --- a/app/views/paragraph_widget/show.html.erb +++ /dev/null @@ -1,7 +0,0 @@ -
-
- <%= scrivito_image_tag widget, :image, class:'image-responsive'%> - <%= scrivito_tag :h1, widget, :headline, data: {newlines: false}, class:'cover-text-white' %> - <%= scrivito_tag :p, widget, :text, class:'cover-text-white', style:'padding-bottom:30px;'%> -
-
\ No newline at end of file diff --git a/app/views/paragraph_widget/thumbnail.html.erb b/app/views/paragraph_widget/thumbnail.html.erb deleted file mode 100644 index 0aabbb1de..000000000 --- a/app/views/paragraph_widget/thumbnail.html.erb +++ /dev/null @@ -1 +0,0 @@ -<%= scrivito_thumbnail ParagraphWidget.description_for_editor %> From eaf1e5bae7da4d1d2ae5c9a07fcf8a109fed18c6 Mon Sep 17 00:00:00 2001 From: Naoya Matayoshi Date: Fri, 2 Dec 2016 23:50:25 +0800 Subject: [PATCH 2/3] Add pre-made index page --- app/assets/stylesheets/application.scss | 4 - app/views/custom_widget/show.html.erb | 139 + app/views/layouts/application.html.erb | 33 +- public/css/style.css | 448 ++ public/font/coderdojo.eot | Bin 0 -> 5264 bytes public/font/coderdojo.ttf | Bin 0 -> 5092 bytes public/font/coderdojo.woff | Bin 0 -> 2376 bytes public/img/coderdojo-japan_cover.jpg | Bin 0 -> 135014 bytes public/js/angular.min.js | 309 + public/js/index.js | 1 + public/js/parse.js | 9629 +++++++++++++++++++++++ 11 files changed, 10557 insertions(+), 6 deletions(-) create mode 100644 public/css/style.css create mode 100644 public/font/coderdojo.eot create mode 100644 public/font/coderdojo.ttf create mode 100644 public/font/coderdojo.woff create mode 100644 public/img/coderdojo-japan_cover.jpg create mode 100644 public/js/angular.min.js create mode 100644 public/js/index.js create mode 100644 public/js/parse.js diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 681502c38..861574862 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -16,7 +16,3 @@ @import "bootstrap-sprockets"; @import "bootstrap"; - -.cover-bg-blue{background-color:#2275CA;} -.cover-text-white{color:#FFFFFF;} -.a {color:#FFFFF;} \ No newline at end of file diff --git a/app/views/custom_widget/show.html.erb b/app/views/custom_widget/show.html.erb index e69de29bb..95a80cfb7 100644 --- a/app/views/custom_widget/show.html.erb +++ b/app/views/custom_widget/show.html.erb @@ -0,0 +1,139 @@ +
+ +

CoderDojo Japan

+

子どものためのプログラミング道場

+
+
+ +
+
+

CoderDojo は7〜17歳の子どもを対象にしたプログラミング道場です。2011年にアイルランドで始まり、世界では66カ国・1,150の道場、日本では全国に67以上の道場があります (2016年12月現在)。

+

各道場では、主に次のような内容を学ぶことができます。

+
    +
  • Scratch (ブロックを組合わせて簡単プログラミング)
  • +
  • HTML / CSS / JavaScript などを活用したWEBサイト作成
  • +
  • Arduinoなどの電子工作と組合わせたプログラミング、など
  • +
+
+

各道場で学べる内容はそれぞれ異なりますが、CoderDojo の雰囲気は一様です。「どんな雰囲気か知りたい」という方は、下記のガイダンス資料をご参考にしてください。

+ +
スライドが見えない場合はコチラから閲覧してください
+ +

さらに詳しい道場の様子については、「CoderDojoとは? 運営者に話を聞いてみた! (バケモノ.jp)」や「子どものための無料プログラミング道場 CoderDojo をはじめてみませんか (ICT教育ニュース)」を読んでみてください。それぞれインタビュー形式で話が構成されているので、より具体的なイメージが掴めるかなと思います :)

+
+ +
+

全国の道場

+ +
道場情報を読み込み中...
+
+ +
+ + +
+ +
+

CoderDojo をもっと知りたい

+

下記ウェブサイトから、より詳細な情報や、最新の情報にアクセスできます。

+ +
+ +

本家サイトである CoderDojo (英語) では世界中の活動の様子が見れます。また、Facebook の CoderDojo Japan では日本全国の活動の様子を知ることができます。

+

「これまでの成り立ちを知りたい」「地元で CoderDojo を立ち上げてみたい」といった場合には CoderDojo Kata を参照してみてください。

+
+

お問い合わせ

+

取材や支援などのお問い合わせについては、下記フォームからお気軽にご連絡ください :)
+メールをご希望の場合は、担当の安川要平まで (yohei@coderdojo.jp) ご連絡ください。

+ + +
+ +
HTML Forms powered by Wufoo.
+ +
+ +
+

CoderDojo Japan 運営グループ

+

本WEBサイトのソースコードや社団法人の定款などは GitHub 上で公開されています。

+

Copyright © 2016 一般社団法人 CoderDojo Japan

+
+ + + + + diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 141e33e4e..b63783a18 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -1,16 +1,45 @@ - CoderdojoJp + + CoderDojo Japan - コーダー道場ジャパン <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %> <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %> <%= csrf_meta_tags %> <%= scrivito_head_tags %> + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
- <%= yield %> <%= scrivito_body_tags %>
diff --git a/public/css/style.css b/public/css/style.css new file mode 100644 index 000000000..5fb80e1ff --- /dev/null +++ b/public/css/style.css @@ -0,0 +1,448 @@ +.cd-fw, body { + text-align: center +} + +.meetup-item a, .mentor-person > header > a { + text-decoration: none +} + +@font-face { + font-family: coderdojo; + src: url(../font/coderdojo.eot); + src: url(../font/coderdojo.eot?#iefix) format('eot'), url(../font/coderdojo.woff) format('woff'), url(../font/coderdojo.ttf) format('truetype'), url(../font/coderdojo.svg#coderdojo) format('svg'); + font-weight: 400; + font-style: normal +} + +.cd:before { + display: inline-block; + font-family: coderdojo; + font-style: normal; + font-weight: 400; + line-height: 1; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale +} + +.cd-lg { + font-size: 1.3333333333333333em; + line-height: .75em; + vertical-align: -15% +} + +.meetup-item, .mentor-person { + overflow: hidden; + vertical-align: top +} + +.cd-2x { + font-size: 2em +} + +.cd-3x { + font-size: 3em +} + +.cd-4x { + font-size: 4em +} + +.cd-5x { + font-size: 5em +} + +.cd-fw { + width: 1.2857142857142858em +} + +.cd-spin { + -webkit-animation: spin 2s infinite linear; + animation: spin 2s infinite linear +} + +.cd-clock:before { + content: "\EA01" +} + +.cd-connect:before { + content: "\EA02" +} + +.cd-glyph07:before { + content: "\EA03" +} + +.cd-glyph08:before { + content: "\EA04" +} + +.cd-logo-jp:before { + content: "\EA05" +} + +.cd-logo:before { + content: "\EA06" +} + +.cd-pin:before { + content: "\EA07" +} + +.cd-spinner:before { + content: "\EA08" +} + +body { + margin: 0; + padding: 0; + font-size: 16px +} + +a { + color: #2e9ad9 +} + +.under-construction { + color: #cd8585; + font-size: 75%; + margin: 0; + padding: .2em; + background: #FFF +} + +body > footer { + margin: -.25em 0 0; + padding: 1.5em 0 4em; + color: #fff +} + +body > footer h2 { + font-size: 87.5%; + margin-top: 0; + margin-bottom: 2em +} + +body > footer a { + color: inherit +} + +.title, body > footer a:hover { + color: #fff +} + +.title { + margin: 0; + padding: 2em +} + +.title > i.cd:before { + font-size: 500%; + display: block +} + +.title > h1 { + font-size: 1.5em; + margin: .5em 0 1em +} + +.title > p { + padding: 0 1em; + margin: 0 auto; + max-width: 680px; + font-size: 130% +} + +@media only screen and (min-width:560px) { + body > footer > ul li { + border-radius: 3px; + line-height: 2.8em + } + + .title > i.cd:before { + font-size: 800% + } + + .title > h1 { + font-size: 1.7em; + margin-bottom: .5em + } + + .title > p { + padding: 0 3em; + font-size: 110% + } +} + +@media only screen and (min-width:720px) { + .title > i.cd:before { + font-size: 1000% + } + + .title > h1 { + font-size: 2.2em; + margin-bottom: .2em + } + + .title > p { + padding: 0 5em; + font-size: 90% + } +} + +body > section { + padding: 1.5em; + background: #fff +} + +body > section > * { + margin: 0 auto; + max-width: 750px +} + +body > section:nth-child(even) { + background: #f7f7f7 +} + +body > section > h1 { + display: none +} + +body > section > h2 { + font-size: 120%; + color: #999; + margin-bottom: .4em +} + +body > section > ul { + text-align: left; + padding-left: 2em; + color: #666 +} + +body > section > p { + margin-bottom: .8em; + text-align: left +} + +.meetup, .meetup > ul { + text-align: center +} + +@media only screen and (min-width:560px) { + body > section > ul { + padding-left: 7em + } + + body > section > p { + text-align: center + } +} + +body > section.cover { + padding: 0; + max-height: 500px; + overflow: hidden; + background: #999; + line-height: 0 +} + +.cover > img { + width: 100%; + max-width: 1200px +} + +.meetup > ul, .mentor > ul { + list-style: none; + max-width: 100% +} + +@media only screen and (min-width:560px) { + .cover > img { + / / margin-top: -100px; + margin-top: -10px + } +} + +@media only screen and (min-width:720px) { + .cover > img { + margin-top: -25px + } +} + +.meetup > ul { + padding: 0 0 1.5em +} + +.meetup-item { + display: inline-block; + width: 85%; + margin: 10px; + background: #fff; + position: relative; + text-align: left +} + +.meetup-item > header { + height: 150px; + position: relative; + overflow: hidden; + background: #c4cce2 +} + +.meetup-item > header > a > img { + width: 100%; + position: absolute; + left: 50%; + margin-left: -50% +} + +.meetup-item > header > a > time { + position: absolute; + left: 0; + bottom: 0; + right: 0; + padding: 10px; + line-height: 1em; + color: #fff; + font-size: 200%; + text-align: center; + background: rgba(0, 0, 0, .4) +} + +.meetup-item h3 { + line-height: 1.3em; + margin: 10px 5px 0; + padding: 0 0 5px; + border-bottom: 1px dashed #ccc; + font-size: 90% +} + +.meetup-item p { + margin: 0; + padding: 10px 5px; + font-size: 90% +} + +.meetup-item-props { + margin: 0; + padding: 10px 5px; + font-size: 90%; + list-style: none +} + +.meetup-item-mentors { + margin: 0; + padding: 0; + list-style: none; + position: absolute; + top: 5px; + right: 5px; + width: 200px; + text-align: right +} + +.meetup-item-mentors > li { + display: inline-block; + margin: 2px +} + +.meetup-item-mentors > li img { + width: 32px; + border: 2px solid #fff; + border-radius: 18px +} + +.meetup-item > footer { + position: absolute; + bottom: 0; + left: 0; + right: 0; + padding: 5px; + font-size: 90%; + color: #ccc; + text-align: center +} + +@media only screen and (min-width:560px) { + .meetup-item { + width: 220px; + height: 305px + } +} + +.mentor > ul { + text-align: center; + padding: 0 0 1.5em +} + +.mentor-person { + display: inline-block; + width: 85%; + margin: 10px; + padding: 15px 0; + background: #fff; + position: relative; + text-align: center +} + +.mentor-person > p { + font-size: 90%; + padding: 5px 10px; + text-align: center; + margin: 5px 0 +} + +.mentor-person-picture { + display: block; + height: 96px; + width: 96px; + background-position: center center; + / / border-radius: 48px; + background-size: cover; + margin: 0 auto +} + +header.title { + background: #2275CA +} + +.mentor-person-name { + display: block; + margin: .5em 0 +} + +@media only screen and (min-width:560px) { + .mentor-person { + width: 220px; + height: 275px + } +} + +.spinner { + height: 3em; + color: #ccc +} + +.tags { + margin: 0 5px; + padding: 0; + list-style: none +} + +.tags > li { + display: inline-block; + font-size: 80%; + color: #fff; + border-radius: .3em; + padding: .2em; + margin: .1em +} + +body { + color: #f; + font-family: 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', 'メイリオ', Meiryo, 'MS Pゴシック', sans-serif +} + +.meetup-item > header time, .title > h1 { + font-family: 'Fjalla One', sans-serif +} diff --git a/public/font/coderdojo.eot b/public/font/coderdojo.eot new file mode 100644 index 0000000000000000000000000000000000000000..80b44bf43090feed9380528760a11bbe78463a65 GIT binary patch literal 5264 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 ZUTP1oY;^GB%HeY3#=6}?hu7is{u@;WBRBv6 literal 0 HcmV?d00001 diff --git a/public/font/coderdojo.woff b/public/font/coderdojo.woff new file mode 100644 index 0000000000000000000000000000000000000000..17fccd19404c8e98b72d8c7cfcd552b9974cb030 GIT binary patch literal 2376 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 P| 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 literal 0 HcmV?d00001 diff --git a/public/js/angular.min.js b/public/js/angular.min.js new file mode 100644 index 000000000..65223c3b8 --- /dev/null +++ b/public/js/angular.min.js @@ -0,0 +1,309 @@ +/* + AngularJS v1.5.2 + (c) 2010-2016 Google, Inc. http://angularjs.org + License: MIT +*/ +(function(N,Q,w){'use strict';function O(a){return function(){var b=arguments[0],d;d="["+(a?a+":":"")+b+"] http://errors.angularjs.org/1.5.2/"+(a?a+"/":"")+b;for(b=1;b").append(a).html();try{return a[0].nodeType===Na?E(d):d.match(/^(<[^>]+>)/)[1].replace(/^<([\w\-]+)/,function(a,b){return"<"+E(b)})}catch(c){return E(d)}}function vc(a){try{return decodeURIComponent(a)}catch(b){}} +function wc(a){var b={};p((a||"").split("&"),function(a){var c,e,f;a&&(e=a=a.replace(/\+/g,"%20"),c=a.indexOf("="),-1!==c&&(e=a.substring(0,c),f=a.substring(c+1)),e=vc(e),A(e)&&(f=A(f)?vc(f):!0,sa.call(b,e)?L(b[e])?b[e].push(f):b[e]=[b[e],f]:b[e]=f))});return b}function Rb(a){var b=[];p(a,function(a,c){L(a)?p(a,function(a){b.push(ja(c,!0)+(!0===a?"":"="+ja(a,!0)))}):b.push(ja(c,!0)+(!0===a?"":"="+ja(a,!0)))});return b.length?b.join("&"):""}function qb(a){return ja(a,!0).replace(/%26/gi,"&").replace(/%3D/gi, +"=").replace(/%2B/gi,"+")}function ja(a,b){return encodeURIComponent(a).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%3B/gi,";").replace(/%20/g,b?"%20":"+")}function ae(a,b){var d,c,e=Oa.length;for(c=0;c/,">"));}b=b||[];b.unshift(["$provide",function(b){b.value("$rootElement",a)}]);d.debugInfoEnabled&&b.push(["$compileProvider",function(a){a.debugInfoEnabled(!0)}]);b.unshift("ng");c=db(b,d.strictDi);c.invoke(["$rootScope", +"$rootElement","$compile","$injector",function(a,b,c,d){a.$apply(function(){b.data("$injector",d);c(b)(a)})}]);return c},e=/^NG_ENABLE_DEBUG_INFO!/,f=/^NG_DEFER_BOOTSTRAP!/;N&&e.test(N.name)&&(d.debugInfoEnabled=!0,N.name=N.name.replace(e,""));if(N&&!f.test(N.name))return c();N.name=N.name.replace(f,"");ea.resumeBootstrap=function(a){p(a,function(a){b.push(a)});return c()};H(ea.resumeDeferredBootstrap)&&ea.resumeDeferredBootstrap()}function ce(){N.name="NG_ENABLE_DEBUG_INFO!"+N.name;N.location.reload()} +function de(a){a=ea.element(a).injector();if(!a)throw Da("test");return a.get("$$testability")}function yc(a,b){b=b||"_";return a.replace(ee,function(a,c){return(c?b:"")+a.toLowerCase()})}function fe(){var a;if(!zc){var b=rb();(ra=v(b)?N.jQuery:b?N[b]:w)&&ra.fn.on?(G=ra,S(ra.fn,{scope:Pa.scope,isolateScope:Pa.isolateScope,controller:Pa.controller,injector:Pa.injector,inheritedData:Pa.inheritedData}),a=ra.cleanData,ra.cleanData=function(b){for(var c,e=0,f;null!=(f=b[e]);e++)(c=ra._data(f,"events"))&& +c.$destroy&&ra(f).triggerHandler("$destroy");a(b)}):G=Y;ea.element=G;zc=!0}}function sb(a,b,d){if(!a)throw Da("areq",b||"?",d||"required");return a}function Qa(a,b,d){d&&L(a)&&(a=a[a.length-1]);sb(H(a),b,"not a function, got "+(a&&"object"===typeof a?a.constructor.name||"Object":typeof a));return a}function Ra(a,b){if("hasOwnProperty"===a)throw Da("badname",b);}function Ac(a,b,d){if(!b)return a;b=b.split(".");for(var c,e=a,f=b.length,g=0;g")+c[2];for(c=c[0];c--;)d=d.lastChild;f=bb(f,d.childNodes);d=e.firstChild;d.textContent=""}else f.push(b.createTextNode(a));e.textContent="";e.innerHTML="";p(f,function(a){e.appendChild(a)});return e}function Lc(a, +b){var d=a.parentNode;d&&d.replaceChild(b,a);b.appendChild(a)}function Y(a){if(a instanceof Y)return a;var b;D(a)&&(a=Z(a),b=!0);if(!(this instanceof Y)){if(b&&"<"!=a.charAt(0))throw Ub("nosel");return new Y(a)}if(b){b=Q;var d;a=(d=Jf.exec(a))?[b.createElement(d[1])]:(d=Kc(a,b))?d.childNodes:[]}Mc(this,a)}function Vb(a){return a.cloneNode(!0)}function wb(a,b){b||fb(a);if(a.querySelectorAll)for(var d=a.querySelectorAll("*"),c=0,e=d.length;c=za?!1:"function"===typeof a&&/^(?:class\s|constructor\()/.test(Function.prototype.toString.call(a));return d?(c.unshift(null),new (Function.prototype.bind.apply(a,c))):a.apply(b,c)},instantiate:function(a,b,c){var d=L(a)?a[a.length-1]:a;a=e(a,b,c);a.unshift(null);return new (Function.prototype.bind.apply(d,a))},get:d,annotate:db.$$annotate,has:function(b){return n.hasOwnProperty(b+ +"Provider")||a.hasOwnProperty(b)}}}b=!0===b;var k={},l=[],m=new Sa([],!0),n={$provide:{provider:d(c),factory:d(f),service:d(function(a,b){return f(a,["$injector",function(a){return a.instantiate(b)}])}),value:d(function(a,b){return f(a,ia(b),!1)}),constant:d(function(a,b){Ra(a,"constant");n[a]=b;B[a]=b}),decorator:function(a,b){var c=y.get(a+"Provider"),d=c.$get;c.$get=function(){var a=x.invoke(d,c);return x.invoke(b,null,{$delegate:a})}}}},y=n.$injector=h(n,function(a,b){ea.isString(b)&&l.push(b); +throw Ga("unpr",l.join(" <- "));}),B={},K=h(B,function(a,b){var c=y.get(a+"Provider",b);return x.invoke(c.$get,c,w,a)}),x=K;n.$injectorProvider={$get:ia(K)};var q=g(a),x=K.get("$injector");x.strictDi=b;p(q,function(a){a&&x.invoke(a)});return x}function Te(){var a=!0;this.disableAutoScrolling=function(){a=!1};this.$get=["$window","$location","$rootScope",function(b,d,c){function e(a){var b=null;Array.prototype.some.call(a,function(a){if("a"===pa(a))return b=a,!0});return b}function f(a){if(a){a.scrollIntoView(); +var c;c=g.yOffset;H(c)?c=c():Ob(c)?(c=c[0],c="fixed"!==b.getComputedStyle(c).position?0:c.getBoundingClientRect().bottom):W(c)||(c=0);c&&(a=a.getBoundingClientRect().top,b.scrollBy(0,a-c))}else b.scrollTo(0,0)}function g(a){a=D(a)?a:d.hash();var b;a?(b=h.getElementById(a))?f(b):(b=e(h.getElementsByName(a)))?f(b):"top"===a&&f(null):f(null)}var h=b.document;a&&c.$watch(function(){return d.hash()},function(a,b){a===b&&""===a||Lf(function(){c.$evalAsync(g)})});return g}]}function hb(a,b){if(!a&&!b)return""; +if(!a)return b;if(!b)return a;L(a)&&(a=a.join(" "));L(b)&&(b=b.join(" "));return a+" "+b}function Uf(a){D(a)&&(a=a.split(" "));var b=X();p(a,function(a){a.length&&(b[a]=!0)});return b}function Ha(a){return I(a)?a:{}}function Vf(a,b,d,c){function e(a){try{a.apply(null,ya.call(arguments,1))}finally{if(K--,0===K)for(;x.length;)try{x.pop()()}catch(b){d.error(b)}}}function f(){M=null;g();h()}function g(){a:{try{q=m.state;break a}catch(a){}q=void 0}q=v(q)?null:q;oa(q,T)&&(q=T);T=q}function h(){if(u!==k.url()|| +t!==q)u=k.url(),t=q,p(C,function(a){a(k.url(),q)})}var k=this,l=a.location,m=a.history,n=a.setTimeout,y=a.clearTimeout,B={};k.isMock=!1;var K=0,x=[];k.$$completeOutstandingRequest=e;k.$$incOutstandingRequestCount=function(){K++};k.notifyWhenNoOutstandingRequests=function(a){0===K?a():x.push(a)};var q,t,u=l.href,s=b.find("base"),M=null;g();t=q;k.url=function(b,d,e){v(e)&&(e=null);l!==a.location&&(l=a.location);m!==a.history&&(m=a.history);if(b){var f=t===e;if(u===b&&(!c.history||f))return k;var h= +u&&Ia(u)===Ia(b);u=b;t=e;if(!c.history||h&&f){if(!h||M)M=b;d?l.replace(b):h?(d=l,e=b.indexOf("#"),e=-1===e?"":b.substr(e),d.hash=e):l.href=b;l.href!==b&&(M=b)}else m[d?"replaceState":"pushState"](e,"",b),g(),t=q;return k}return M||l.href.replace(/%27/g,"'")};k.state=function(){return q};var C=[],J=!1,T=null;k.onUrlChange=function(b){if(!J){if(c.history)G(a).on("popstate",f);G(a).on("hashchange",f);J=!0}C.push(b);return b};k.$$applicationDestroyed=function(){G(a).off("hashchange popstate",f)};k.$$checkUrlChange= +h;k.baseHref=function(){var a=s.attr("href");return a?a.replace(/^(https?\:)?\/\/[^\/]*/,""):""};k.defer=function(a,b){var c;K++;c=n(function(){delete B[c];e(a)},b||0);B[c]=!0;return c};k.defer.cancel=function(a){return B[a]?(delete B[a],y(a),e(z),!0):!1}}function $e(){this.$get=["$window","$log","$sniffer","$document",function(a,b,d,c){return new Vf(a,c,b,d)}]}function af(){this.$get=function(){function a(a,c){function e(a){a!=n&&(y?y==a&&(y=a.n):y=a,f(a.n,a.p),f(a,n),n=a,n.n=null)}function f(a, +b){a!=b&&(a&&(a.p=b),b&&(b.n=a))}if(a in b)throw O("$cacheFactory")("iid",a);var g=0,h=S({},c,{id:a}),k=X(),l=c&&c.capacity||Number.MAX_VALUE,m=X(),n=null,y=null;return b[a]={put:function(a,b){if(!v(b)){if(ll&&this.remove(y.key);return b}},get:function(a){if(l";b=fa.firstChild.attributes;var d=b[0];b.removeNamedItem(d.name);d.value=c;a.attributes.setNamedItem(d)}function $(a,b){try{a.addClass(b)}catch(c){}}function R(a,b,c,d,e){a instanceof G||(a=G(a));for(var f=/\S+/,g=0,h=a.length;g").append(a).html())):c?Pa.clone.call(a):a;if(g)for(var h in g)d.data("$"+h+"Controller",g[h].instance);R.$$addScopeInfo(d, +b);c&&c(d,b);l&&l(b,d,d,f);return d}}function P(a,b,c,d,e,f){function g(a,c,d,e){var f,k,l,m,n,C,u;if(q)for(u=Array(c.length),m=0;ms.priority)break;if(E=s.scope)s.templateUrl||(I(E)?(aa("new/isolated scope",t||C,s,P),t=s):aa("new/isolated scope",t,s,P)),C=C||s;K=s.name;if(!va&&(s.replace&&(s.templateUrl||s.template)||s.transclude&& +!s.$$tlb)){for(E=Q+1;va=a[E++];)if(va.transclude&&!va.$$tlb||va.replace&&(va.templateUrl||va.template)){N=!0;break}va=!0}!s.templateUrl&&s.controller&&(E=s.controller,u=u||X(),aa("'"+K+"' controller",u[K],s,P),u[K]=s);if(E=s.transclude)if(F=!0,s.$$tlb||(aa("transclusion",B,s,P),B=s),"element"==E)M=!0,q=s.priority,ca=P,P=d.$$element=G(R.$$createComment(K,d[K])),b=P[0],ga(f,ya.call(ca,0),b),z=Yb(N,ca,e,q,g&&g.name,{nonTlbTranscludeDirective:B});else{var U=X();ca=G(Vb(b)).contents();if(I(E)){ca=[];var ea= +X(),fa=X();p(E,function(a,b){var c="?"===a.charAt(0);a=c?a.substring(1):a;ea[a]=b;U[b]=null;fa[b]=c});p(P.contents(),function(a){var b=ea[ua(pa(a))];b?(fa[b]=!0,U[b]=U[b]||[],U[b].push(a)):ca.push(a)});p(fa,function(a,b){if(!a)throw la("reqslot",b);});for(var ha in U)U[ha]&&(U[ha]=Yb(N,U[ha],e))}P.empty();z=Yb(N,ca,e,w,w,{needsNewScope:s.$$isolateScope||s.$$newScope});z.$$slots=U}if(s.template)if($=!0,aa("template",J,s,P),J=s,E=H(s.template)?s.template(P,d):s.template,E=ra(E),s.replace){g=s;ca=Tb.test(E)? +Vc(da(s.templateNamespace,Z(E))):[];b=ca[0];if(1!=ca.length||1!==b.nodeType)throw la("tplrt",K,"");ga(f,P,b);Ua={$attr:{}};E=Aa(b,[],Ua);var ka=a.splice(Q+1,a.length-(Q+1));(t||C)&&Wc(E,t,C);a=a.concat(E).concat(ka);W(d,Ua);Ua=a.length}else P.html(E);if(s.templateUrl)$=!0,aa("template",J,s,P),J=s,s.replace&&(g=s),n=Y(a.splice(Q,a.length-Q),P,d,f,F&&z,h,k,{controllerDirectives:u,newScopeDirective:C!==s&&C,newIsolateScopeDirective:t,templateDirective:J,nonTlbTranscludeDirective:B}),Ua=a.length;else if(s.compile)try{D= +s.compile(P,d,z),H(D)?m(null,D,V,ba):D&&m(D.pre,D.post,V,ba)}catch(Wf){c(Wf,ta(P))}s.terminal&&(n.terminal=!0,q=Math.max(q,s.priority))}n.scope=C&&!0===C.scope;n.transcludeOnThisElement=F;n.templateOnThisElement=$;n.transclude=z;l.hasElementTranscludeDirective=M;return n}function ib(a,b,c,d){var e;if(D(b)){var f=b.match(k);b=b.substring(f[0].length);var g=f[1]||f[3],f="?"===f[2];"^^"===g?c=c.parent():e=(e=d&&d[b])&&e.instance;if(!e){var h="$"+b+"Controller";e=g?c.inheritedData(h):c.data(h)}if(!e&& +!f)throw la("ctreq",b,a);}else if(L(b))for(e=[],g=0,f=b.length;gn.priority)&&-1!=n.restrict.indexOf(g)){l&&(n=Pb(n,{$$start:l,$$end:m}));if(!n.$$bindings){var u=n,t=n,J=n.name,s={isolateScope:null,bindToController:null};I(t.scope)&&(!0===t.bindToController?(s.bindToController=d(t.scope,J,!0),s.isolateScope={}):s.isolateScope=d(t.scope,J,!1));I(t.bindToController)&&(s.bindToController=d(t.bindToController, +J,!0));if(I(s.bindToController)){var F=t.controller,$=t.controllerAs;if(!F)throw la("noctrl",J);if(!Tc(F,$))throw la("noident",J);}var T=u.$$bindings=s;I(T.isolateScope)&&(n.$$isolateBindings=T.isolateScope)}b.push(n);k=n}}catch(P){c(P)}}return k}function va(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 U(a,b){if("srcdoc"==b)return M.HTML;var c=pa(a);if("xlinkHref"== +b||"form"==c&&"action"==b||"img"!=c&&("src"==b||"ngSrc"==b))return M.RESOURCE_URL}function ea(a,c,d,e,f){var g=U(a,e);f=h[e]||f;var k=b(d,!0,g,f);if(k){if("multiple"===e&&"select"===pa(a))throw la("selmulti",ta(a));c.push({priority:100,compile:function(){return{pre:function(a,c,h){c=h.$$observers||(h.$$observers=X());if(l.test(e))throw la("nodomevents");var m=h[e];m!==d&&(k=m&&b(m,!0,g,f),d=m);k&&(h[e]=k(a),(c[e]||(c[e]=[])).$$inter=!0,(h.$$observers&&h.$$observers[e].$$scope||a).$watch(k,function(a, +b){"class"===e&&a!=b?h.$updateClass(a,b):h.$set(e,a)}))}}}})}}function ga(a,b,c){var d=b[0],e=b.length,f=d.parentNode,g,h;if(a)for(g=0,h=a.length;g=b)return a;for(;b--;)8===a[b].nodeType&&Yf.call(a,b,1);return a}function Tc(a,b){if(b&&D(b))return b;if(D(a)){var d=Zc.exec(a);if(d)return d[3]}}function bf(){var a={},b=!1;this.has=function(b){return a.hasOwnProperty(b)};this.register= +function(b,c){Ra(b,"controller");I(b)?S(a,b):a[b]=c};this.allowGlobals=function(){b=!0};this.$get=["$injector","$window",function(d,c){function e(a,b,c,d){if(!a||!I(a.$scope))throw O("$controller")("noscp",d,b);a.$scope[b]=c}return function(f,g,h,k){var l,m,n;h=!0===h;k&&D(k)&&(n=k);if(D(f)){k=f.match(Zc);if(!k)throw Zf("ctrlfmt",f);m=k[1];n=n||k[3];f=a.hasOwnProperty(m)?a[m]:Ac(g.$scope,m,!0)||(b?Ac(c,m,!0):w);Qa(f,m,!0)}if(h)return h=(L(f)?f[f.length-1]:f).prototype,l=Object.create(h||null),n&& +e(g,n,l,m||f.name),S(function(){var a=d.invoke(f,l,g,m);a!==l&&(I(a)||H(a))&&(l=a,n&&e(g,n,l,m||f.name));return l},{instance:l,identifier:n});l=d.instantiate(f,g,m);n&&e(g,n,l,m||f.name);return l}}]}function cf(){this.$get=["$window",function(a){return G(a.document)}]}function df(){this.$get=["$log",function(a){return function(b,d){a.error.apply(a,arguments)}}]}function Zb(a){return I(a)?ga(a)?a.toISOString():cb(a):a}function jf(){this.$get=function(){return function(a){if(!a)return"";var b=[];oc(a, +function(a,c){null===a||v(a)||(L(a)?p(a,function(a){b.push(ja(c)+"="+ja(Zb(a)))}):b.push(ja(c)+"="+ja(Zb(a))))});return b.join("&")}}}function kf(){this.$get=function(){return function(a){function b(a,e,f){null===a||v(a)||(L(a)?p(a,function(a,c){b(a,e+"["+(I(a)?c:"")+"]")}):I(a)&&!ga(a)?oc(a,function(a,c){b(a,e+(f?"":"[")+c+(f?"":"]"))}):d.push(ja(e)+"="+ja(Zb(a))))}if(!a)return"";var d=[];b(a,"",!0);return d.join("&")}}}function $b(a,b){if(D(a)){var d=a.replace($f,"").trim();if(d){var c=b("Content-Type"); +(c=c&&0===c.indexOf($c))||(c=(c=d.match(ag))&&bg[c[0]].test(d));c&&(a=tc(d))}}return a}function ad(a){var b=X(),d;D(a)?p(a.split("\n"),function(a){d=a.indexOf(":");var e=E(Z(a.substr(0,d)));a=Z(a.substr(d+1));e&&(b[e]=b[e]?b[e]+", "+a:a)}):I(a)&&p(a,function(a,d){var f=E(d),g=Z(a);f&&(b[f]=b[f]?b[f]+", "+g:g)});return b}function bd(a){var b;return function(d){b||(b=ad(a));return d?(d=b[E(d)],void 0===d&&(d=null),d):b}}function cd(a,b,d,c){if(H(c))return c(a,b,d);p(c,function(c){a=c(a,b,d)});return a} +function hf(){var a=this.defaults={transformResponse:[$b],transformRequest:[function(a){return I(a)&&"[object File]"!==ha.call(a)&&"[object Blob]"!==ha.call(a)&&"[object FormData]"!==ha.call(a)?cb(a):a}],headers:{common:{Accept:"application/json, text/plain, */*"},post:ma(ac),put:ma(ac),patch:ma(ac)},xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",paramSerializer:"$httpParamSerializer"},b=!1;this.useApplyAsync=function(a){return A(a)?(b=!!a,this):b};var d=!0;this.useLegacyPromiseExtensions= +function(a){return A(a)?(d=!!a,this):d};var c=this.interceptors=[];this.$get=["$httpBackend","$$cookieReader","$cacheFactory","$rootScope","$q","$injector",function(e,f,g,h,k,l){function m(b){function c(a){var b=S({},a);b.data=cd(a.data,a.headers,a.status,f.transformResponse);a=a.status;return 200<=a&&300>a?b:k.reject(b)}function e(a,b){var c,d={};p(a,function(a,e){H(a)?(c=a(b),null!=c&&(d[e]=c)):d[e]=a});return d}if(!I(b))throw O("$http")("badreq",b);if(!D(b.url))throw O("$http")("badreq",b.url); +var f=S({method:"get",transformRequest:a.transformRequest,transformResponse:a.transformResponse,paramSerializer:a.paramSerializer},b);f.headers=function(b){var c=a.headers,d=S({},b.headers),f,g,h,c=S({},c.common,c[E(b.method)]);a:for(f in c){g=E(f);for(h in d)if(E(h)===g)continue a;d[f]=c[f]}return e(d,ma(b))}(b);f.method=ub(f.method);f.paramSerializer=D(f.paramSerializer)?l.get(f.paramSerializer):f.paramSerializer;var g=[function(b){var d=b.headers,e=cd(b.data,bd(d),w,b.transformRequest);v(e)&&p(d, +function(a,b){"content-type"===E(b)&&delete d[b]});v(b.withCredentials)&&!v(a.withCredentials)&&(b.withCredentials=a.withCredentials);return n(b,e).then(c,c)},w],h=k.when(f);for(p(K,function(a){(a.request||a.requestError)&&g.unshift(a.request,a.requestError);(a.response||a.responseError)&&g.push(a.response,a.responseError)});g.length;){b=g.shift();var m=g.shift(),h=h.then(b,m)}d?(h.success=function(a){Qa(a,"fn");h.then(function(b){a(b.data,b.status,b.headers,f)});return h},h.error=function(a){Qa(a, +"fn");h.then(null,function(b){a(b.data,b.status,b.headers,f)});return h}):(h.success=dd("success"),h.error=dd("error"));return h}function n(c,d){function g(a,c,d,e){function f(){l(c,a,d,e)}T&&(200<=a&&300>a?T.put(R,[a,c,ad(d),e]):T.remove(R));b?h.$applyAsync(f):(f(),h.$$phase||h.$apply())}function l(a,b,d,e){b=-1<=b?b:0;(200<=b&&300>b?C.resolve:C.reject)({data:a,status:b,headers:bd(d),config:c,statusText:e})}function n(a){l(a.data,a.status,ma(a.headers()),a.statusText)}function K(){var a=m.pendingRequests.indexOf(c); +-1!==a&&m.pendingRequests.splice(a,1)}var C=k.defer(),J=C.promise,T,F,$=c.headers,R=y(c.url,c.paramSerializer(c.params));m.pendingRequests.push(c);J.then(K,K);!c.cache&&!a.cache||!1===c.cache||"GET"!==c.method&&"JSONP"!==c.method||(T=I(c.cache)?c.cache:I(a.cache)?a.cache:B);T&&(F=T.get(R),A(F)?F&&H(F.then)?F.then(n,n):L(F)?l(F[1],F[0],ma(F[2]),F[3]):l(F,200,{},"OK"):T.put(R,J));v(F)&&((F=ed(c.url)?f()[c.xsrfCookieName||a.xsrfCookieName]:w)&&($[c.xsrfHeaderName||a.xsrfHeaderName]=F),e(c.method,R,d, +g,$,c.timeout,c.withCredentials,c.responseType));return J}function y(a,b){0=l&&(u.resolve(q),x(s.$$intervalId),delete g[s.$$intervalId]);t||a.$apply()},k);g[s.$$intervalId]=u;return s}var g={};f.cancel=function(a){return a&&a.$$intervalId in g?(g[a.$$intervalId].reject("canceled"),b.clearInterval(a.$$intervalId),delete g[a.$$intervalId],!0):!1};return f}]}function bc(a){a=a.split("/");for(var b=a.length;b--;)a[b]=qb(a[b]);return a.join("/")}function fd(a,b){var d=wa(a);b.$$protocol=d.protocol;b.$$host=d.hostname; +b.$$port=da(d.port)||dg[d.protocol]||null}function gd(a,b){var d="/"!==a.charAt(0);d&&(a="/"+a);var c=wa(a);b.$$path=decodeURIComponent(d&&"/"===c.pathname.charAt(0)?c.pathname.substring(1):c.pathname);b.$$search=wc(c.search);b.$$hash=decodeURIComponent(c.hash);b.$$path&&"/"!=b.$$path.charAt(0)&&(b.$$path="/"+b.$$path)}function na(a,b){if(0===b.indexOf(a))return b.substr(a.length)}function Ia(a){var b=a.indexOf("#");return-1==b?a:a.substr(0,b)}function jb(a){return a.replace(/(#.+)|#$/,"$1")}function cc(a, +b,d){this.$$html5=!0;d=d||"";fd(a,this);this.$$parse=function(a){var d=na(b,a);if(!D(d))throw Eb("ipthprfx",a,b);gd(d,this);this.$$path||(this.$$path="/");this.$$compose()};this.$$compose=function(){var a=Rb(this.$$search),d=this.$$hash?"#"+qb(this.$$hash):"";this.$$url=bc(this.$$path)+(a?"?"+a:"")+d;this.$$absUrl=b+this.$$url.substr(1)};this.$$parseLinkUrl=function(c,e){if(e&&"#"===e[0])return this.hash(e.slice(1)),!0;var f,g;A(f=na(a,c))?(g=f,g=A(f=na(d,f))?b+(na("/",f)||f):a+g):A(f=na(b,c))?g= +b+f:b==c+"/"&&(g=b);g&&this.$$parse(g);return!!g}}function dc(a,b,d){fd(a,this);this.$$parse=function(c){var e=na(a,c)||na(b,c),f;v(e)||"#"!==e.charAt(0)?this.$$html5?f=e:(f="",v(e)&&(a=c,this.replace())):(f=na(d,e),v(f)&&(f=e));gd(f,this);c=this.$$path;var e=a,g=/^\/[A-Z]:(\/.*)/;0===f.indexOf(e)&&(f=f.replace(e,""));g.exec(f)||(c=(f=g.exec(c))?f[1]:c);this.$$path=c;this.$$compose()};this.$$compose=function(){var b=Rb(this.$$search),e=this.$$hash?"#"+qb(this.$$hash):"";this.$$url=bc(this.$$path)+ +(b?"?"+b:"")+e;this.$$absUrl=a+(this.$$url?d+this.$$url:"")};this.$$parseLinkUrl=function(b,d){return Ia(a)==Ia(b)?(this.$$parse(b),!0):!1}}function hd(a,b,d){this.$$html5=!0;dc.apply(this,arguments);this.$$parseLinkUrl=function(c,e){if(e&&"#"===e[0])return this.hash(e.slice(1)),!0;var f,g;a==Ia(c)?f=c:(g=na(b,c))?f=a+d+g:b===c+"/"&&(f=b);f&&this.$$parse(f);return!!f};this.$$compose=function(){var b=Rb(this.$$search),e=this.$$hash?"#"+qb(this.$$hash):"";this.$$url=bc(this.$$path)+(b?"?"+b:"")+e;this.$$absUrl= +a+d+this.$$url}}function Fb(a){return function(){return this[a]}}function id(a,b){return function(d){if(v(d))return this[a];this[a]=b(d);this.$$compose();return this}}function nf(){var a="",b={enabled:!1,requireBase:!0,rewriteLinks:!0};this.hashPrefix=function(b){return A(b)?(a=b,this):a};this.html5Mode=function(a){return Ma(a)?(b.enabled=a,this):I(a)?(Ma(a.enabled)&&(b.enabled=a.enabled),Ma(a.requireBase)&&(b.requireBase=a.requireBase),Ma(a.rewriteLinks)&&(b.rewriteLinks=a.rewriteLinks),this):b}; +this.$get=["$rootScope","$browser","$sniffer","$rootElement","$window",function(d,c,e,f,g){function h(a,b,d){var e=l.url(),f=l.$$state;try{c.url(a,b,d),l.$$state=c.state()}catch(g){throw l.url(e),l.$$state=f,g;}}function k(a,b){d.$broadcast("$locationChangeSuccess",l.absUrl(),a,l.$$state,b)}var l,m;m=c.baseHref();var n=c.url(),y;if(b.enabled){if(!m&&b.requireBase)throw Eb("nobase");y=n.substring(0,n.indexOf("/",n.indexOf("//")+2))+(m||"/");m=e.history?cc:hd}else y=Ia(n),m=dc;var B=y.substr(0,Ia(y).lastIndexOf("/")+ +1);l=new m(y,B,"#"+a);l.$$parseLinkUrl(n,n);l.$$state=c.state();var p=/^\s*(javascript|mailto):/i;f.on("click",function(a){if(b.rewriteLinks&&!a.ctrlKey&&!a.metaKey&&!a.shiftKey&&2!=a.which&&2!=a.button){for(var e=G(a.target);"a"!==pa(e[0]);)if(e[0]===f[0]||!(e=e.parent())[0])return;var h=e.prop("href"),k=e.attr("href")||e.attr("xlink:href");I(h)&&"[object SVGAnimatedString]"===h.toString()&&(h=wa(h.animVal).href);p.test(h)||!h||e.attr("target")||a.isDefaultPrevented()||!l.$$parseLinkUrl(h,k)||(a.preventDefault(), +l.absUrl()!=c.url()&&(d.$apply(),g.angular["ff-684208-preventDefault"]=!0))}});jb(l.absUrl())!=jb(n)&&c.url(l.absUrl(),!0);var x=!0;c.onUrlChange(function(a,b){v(na(B,a))?g.location.href=a:(d.$evalAsync(function(){var c=l.absUrl(),e=l.$$state,f;a=jb(a);l.$$parse(a);l.$$state=b;f=d.$broadcast("$locationChangeStart",a,c,b,e).defaultPrevented;l.absUrl()===a&&(f?(l.$$parse(c),l.$$state=e,h(c,!1,e)):(x=!1,k(c,e)))}),d.$$phase||d.$digest())});d.$watch(function(){var a=jb(c.url()),b=jb(l.absUrl()),f=c.state(), +g=l.$$replace,m=a!==b||l.$$html5&&e.history&&f!==l.$$state;if(x||m)x=!1,d.$evalAsync(function(){var b=l.absUrl(),c=d.$broadcast("$locationChangeStart",b,a,l.$$state,f).defaultPrevented;l.absUrl()===b&&(c?(l.$$parse(a),l.$$state=f):(m&&h(b,g,f===l.$$state?null:l.$$state),k(a,f)))});l.$$replace=!1});return l}]}function of(){var a=!0,b=this;this.debugEnabled=function(b){return A(b)?(a=b,this):a};this.$get=["$window",function(d){function c(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=d.console||{},e=b[a]||b.log||z;a=!1;try{a=!!e.apply}catch(k){}return a?function(){var a=[];p(arguments,function(b){a.push(c(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(){a&&c.apply(b,arguments)}}()}}]}function Va(a,b){if("__defineGetter__"=== +a||"__defineSetter__"===a||"__lookupGetter__"===a||"__lookupSetter__"===a||"__proto__"===a)throw U("isecfld",b);return a}function eg(a){return a+""}function xa(a,b){if(a){if(a.constructor===a)throw U("isecfn",b);if(a.window===a)throw U("isecwindow",b);if(a.children&&(a.nodeName||a.prop&&a.attr&&a.find))throw U("isecdom",b);if(a===Object)throw U("isecobj",b);}return a}function jd(a,b){if(a){if(a.constructor===a)throw U("isecfn",b);if(a===fg||a===gg||a===hg)throw U("isecff",b);}}function Gb(a,b){if(a&& +(a===(0).constructor||a===(!1).constructor||a==="".constructor||a==={}.constructor||a===[].constructor||a===Function.constructor))throw U("isecaf",b);}function ig(a,b){return"undefined"!==typeof a?a:b}function kd(a,b){return"undefined"===typeof a?b:"undefined"===typeof b?a:a+b}function V(a,b){var d,c;switch(a.type){case r.Program:d=!0;p(a.body,function(a){V(a.expression,b);d=d&&a.expression.constant});a.constant=d;break;case r.Literal:a.constant=!0;a.toWatch=[];break;case r.UnaryExpression:V(a.argument, +b);a.constant=a.argument.constant;a.toWatch=a.argument.toWatch;break;case r.BinaryExpression:V(a.left,b);V(a.right,b);a.constant=a.left.constant&&a.right.constant;a.toWatch=a.left.toWatch.concat(a.right.toWatch);break;case r.LogicalExpression:V(a.left,b);V(a.right,b);a.constant=a.left.constant&&a.right.constant;a.toWatch=a.constant?[]:[a];break;case r.ConditionalExpression:V(a.test,b);V(a.alternate,b);V(a.consequent,b);a.constant=a.test.constant&&a.alternate.constant&&a.consequent.constant;a.toWatch= +a.constant?[]:[a];break;case r.Identifier:a.constant=!1;a.toWatch=[a];break;case r.MemberExpression:V(a.object,b);a.computed&&V(a.property,b);a.constant=a.object.constant&&(!a.computed||a.property.constant);a.toWatch=[a];break;case r.CallExpression:d=a.filter?!b(a.callee.name).$stateful:!1;c=[];p(a.arguments,function(a){V(a,b);d=d&&a.constant;a.constant||c.push.apply(c,a.toWatch)});a.constant=d;a.toWatch=a.filter&&!b(a.callee.name).$stateful?c:[a];break;case r.AssignmentExpression:V(a.left,b);V(a.right, +b);a.constant=a.left.constant&&a.right.constant;a.toWatch=[a];break;case r.ArrayExpression:d=!0;c=[];p(a.elements,function(a){V(a,b);d=d&&a.constant;a.constant||c.push.apply(c,a.toWatch)});a.constant=d;a.toWatch=c;break;case r.ObjectExpression:d=!0;c=[];p(a.properties,function(a){V(a.value,b);d=d&&a.value.constant;a.value.constant||c.push.apply(c,a.value.toWatch)});a.constant=d;a.toWatch=c;break;case r.ThisExpression:a.constant=!1;a.toWatch=[];break;case r.LocalsExpression:a.constant=!1,a.toWatch= +[]}}function ld(a){if(1==a.length){a=a[0].expression;var b=a.toWatch;return 1!==b.length?b:b[0]!==a?b:w}}function md(a){return a.type===r.Identifier||a.type===r.MemberExpression}function nd(a){if(1===a.body.length&&md(a.body[0].expression))return{type:r.AssignmentExpression,left:a.body[0].expression,right:{type:r.NGValueParameter},operator:"="}}function od(a){return 0===a.body.length||1===a.body.length&&(a.body[0].expression.type===r.Literal||a.body[0].expression.type===r.ArrayExpression||a.body[0].expression.type=== +r.ObjectExpression)}function pd(a,b){this.astBuilder=a;this.$filter=b}function qd(a,b){this.astBuilder=a;this.$filter=b}function Hb(a){return"constructor"==a}function ec(a){return H(a.valueOf)?a.valueOf():jg.call(a)}function pf(){var a=X(),b=X(),d={"true":!0,"false":!1,"null":null,undefined:w};this.addLiteral=function(a,b){d[a]=b};this.$get=["$filter",function(c){function e(d,e,g){var y,p,C;g=g||x;switch(typeof d){case "string":C=d=d.trim();var J=g?b:a;y=J[C];if(!y){":"===d.charAt(0)&&":"===d.charAt(1)&& +(p=!0,d=d.substring(2));y=g?K:B;var T=new fc(y);y=(new gc(T,c,y)).parse(d);y.constant?y.$$watchDelegate=m:p?y.$$watchDelegate=y.literal?l:k:y.inputs&&(y.$$watchDelegate=h);g&&(y=f(y));J[C]=y}return n(y,e);case "function":return n(d,e);default:return n(z,e)}}function f(a){function b(c,d,e,f){var g=x;x=!0;try{return a(c,d,e,f)}finally{x=g}}if(!a)return a;b.$$watchDelegate=a.$$watchDelegate;b.assign=f(a.assign);b.constant=a.constant;b.literal=a.literal;for(var c=0;a.inputs&&c=this.promise.$$state.status&&d&&d.length&&a(function(){for(var a,e,f=0,g=d.length;fa)for(b in l++,f)sa.call(e,b)||(u--,delete f[b])}else f!==e&&(f=e,l++);return l}}c.$stateful=!0;var d=this,e,f,h,k=1p&&(A=4-p,w[A]||(w[A]=[]),w[A].push({msg:H(a.exp)?"fn: "+(a.exp.name||a.exp.toString()):a.exp,newVal:g,oldVal:k}));else if(a===c){B=!1;break a}}catch(G){f(G)}if(!(y=M.$$watchersCount&&M.$$childHead||M!==this&&M.$$nextSibling))for(;M!==this&&!(y=M.$$nextSibling);)M=M.$parent}while(M=y);if((B||u.length)&&!p--)throw t.$$phase=null,d("infdig",b,w);}while(B||u.length); +for(t.$$phase=null;s.length;)try{s.shift()()}catch(D){f(D)}},$destroy:function(){if(!this.$$destroyed){var a=this.$parent;this.$broadcast("$destroy");this.$$destroyed=!0;this===t&&h.$$applicationDestroyed();y(this,-this.$$watchersCount);for(var b in this.$$listenerCount)B(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=z;this.$on=this.$watch=this.$watchGroup=function(){return z};this.$$listeners={};this.$$nextSibling=null;l(this)}},$eval:function(a,b){return g(a)(this,b)},$evalAsync:function(a,b){t.$$phase||u.length||h.defer(function(){u.length&&t.$digest()});u.push({scope:this,expression:g(a),locals:b})},$$postDigest:function(a){s.push(a)},$apply:function(a){try{n("$apply");try{return this.$eval(a)}finally{t.$$phase= +null}}catch(b){f(b)}finally{try{t.$digest()}catch(c){throw f(c),c;}}},$applyAsync:function(a){function b(){c.$eval(a)}var c=this;a&&M.push(b);a=g(a);q()},$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,B(e,1,a))}},$emit:function(a,b){var c=[],d,e=this,g=!1,h={name:a,targetScope:e,stopPropagation:function(){g= +!0},preventDefault:function(){h.defaultPrevented=!0},defaultPrevented:!1},k=bb([h],arguments,1),l,m;do{d=e.$$listeners[a]||c;h.currentScope=e;l=0;for(m=d.length;lza)throw Ba("iequirks");var c=ma(fa);c.isEnabled=function(){return a};c.trustAs=d.trustAs;c.getTrusted=d.getTrusted;c.valueOf=d.valueOf;a||(c.trustAs=c.getTrusted=function(a,b){return b},c.valueOf=Za);c.parseAs=function(a,d){var e=b(d);return e.literal&&e.constant?e:b(d,function(b){return c.getTrusted(a,b)})};var e=c.parseAs,f=c.getTrusted,g=c.trustAs;p(fa,function(a,b){var d=E(b);c[eb("parse_as_"+d)]=function(b){return e(a, +b)};c[eb("get_trusted_"+d)]=function(b){return f(a,b)};c[eb("trust_as_"+d)]=function(b){return g(a,b)}});return c}]}function vf(){this.$get=["$window","$document",function(a,b){var d={},c=da((/android (\d+)/.exec(E((a.navigator||{}).userAgent))||[])[1]),e=/Boxee/i.test((a.navigator||{}).userAgent),f=b[0]||{},g,h=/^(Moz|webkit|ms)(?=[A-Z])/,k=f.body&&f.body.style,l=!1,m=!1;if(k){for(var n in k)if(l=h.exec(n)){g=l[0];g=g.substr(0,1).toUpperCase()+g.substr(1);break}g||(g="WebkitOpacity"in k&&"webkit"); +l=!!("transition"in k||g+"Transition"in k);m=!!("animation"in k||g+"Animation"in k);!c||l&&m||(l=D(k.webkitTransition),m=D(k.webkitAnimation))}return{history:!(!a.history||!a.history.pushState||4>c||e),hasEvent:function(a){if("input"===a&&11>=za)return!1;if(v(d[a])){var b=f.createElement("div");d[a]="on"+a in b}return d[a]},csp:Ea(),vendorPrefix:g,transitions:l,animations:m,android:c}}]}function xf(){var a;this.httpOptions=function(b){return b?(a=b,this):a};this.$get=["$templateCache","$http","$q", +"$sce",function(b,d,c,e){function f(g,h){f.totalPendingRequests++;D(g)&&b.get(g)||(g=e.getTrustedResourceUrl(g));var k=d.defaults&&d.defaults.transformResponse;L(k)?k=k.filter(function(a){return a!==$b}):k===$b&&(k=null);return d.get(g,S({cache:b,transformResponse:k},a))["finally"](function(){f.totalPendingRequests--}).then(function(a){b.put(g,a.data);return a.data},function(a){if(!h)throw lg("tpload",g,a.status,a.statusText);return c.reject(a)})}f.totalPendingRequests=0;return f}]}function yf(){this.$get= +["$rootScope","$browser","$location",function(a,b,d){return{findBindings:function(a,b,d){a=a.getElementsByClassName("ng-binding");var g=[];p(a,function(a){var c=ea.element(a).data("$binding");c&&p(c,function(c){d?(new RegExp("(^|\\s)"+sd(b)+"(\\s|\\||$)")).test(c)&&g.push(a):-1!=c.indexOf(b)&&g.push(a)})});return g},findModels:function(a,b,d){for(var g=["ng-","data-ng-","ng\\:"],h=0;hc&&(c=e),c+=+a.slice(e+1),a=a.substring(0,e)):0>c&&(c=a.length);for(e=0;a.charAt(e)==ic;e++);if(e==(g=a.length))d=[0],c=1;else{for(g--;a.charAt(g)==ic;)g--;c-=e;d=[];for(f=0;e<=g;e++,f++)d[f]=+a.charAt(e)}c>Cd&&(d=d.splice(0,Cd-1),b=c-1, +c=1);return{d:d,e:b,i:c}}function tg(a,b,d,c){var e=a.d,f=e.length-a.i;b=v(b)?Math.min(Math.max(d,f),c):+b;d=b+a.i;c=e[d];if(0d-1){for(c=0;c>d;c--)e.unshift(0),a.i++;e.unshift(1);a.i++}else e[d-1]++;for(;fh;)k.unshift(0),h++;0b.lgSize&&h.unshift(k.splice(-b.lgSize).join(""));k.length>b.gSize;)h.unshift(k.splice(-b.gSize).join(""));k.length&&h.unshift(k.join(""));k=h.join(d);f.length&&(k+=c+f.join(""));e&&(k+="e+"+e)}return 0>a&&!g?b.negPre+ +k+b.negSuf:b.posPre+k+b.posSuf}function Ib(a,b,d,c){var e="";if(0>a||c&&0>=a)c?a=-a+1:(a=-a,e="-");for(a=""+a;a.length-d)f+=d;0===f&&-12==d&&(f=12);return Ib(f,b,c,e)}}function kb(a,b,d){return function(c,e){var f=c["get"+a](),g=ub((d?"STANDALONE":"")+(b?"SHORT":"")+a);return e[g][f]}}function Dd(a){var b=(new Date(a,0,1)).getDay();return new Date(a,0,(4>=b?5:12)-b)}function Ed(a){return function(b){var d= +Dd(b.getFullYear());b=+new Date(b.getFullYear(),b.getMonth(),b.getDate()+(4-b.getDay()))-+d;b=1+Math.round(b/6048E5);return Ib(b,a)}}function jc(a,b){return 0>=a.getFullYear()?b.ERAS[0]:b.ERAS[1]}function xd(a){function b(a){var b;if(b=a.match(d)){a=new Date(0);var f=0,g=0,h=b[8]?a.setUTCFullYear:a.setFullYear,k=b[8]?a.setUTCHours:a.setHours;b[9]&&(f=da(b[9]+b[10]),g=da(b[9]+b[11]));h.call(a,da(b[1]),da(b[2])-1,da(b[3]));f=da(b[4]||0)-f;g=da(b[5]||0)-g;h=da(b[6]||0);b=Math.round(1E3*parseFloat("0."+ +(b[7]||0)));k.call(a,f,g,h,b)}return a}var d=/^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;return function(c,d,f){var g="",h=[],k,l;d=d||"mediumDate";d=a.DATETIME_FORMATS[d]||d;D(c)&&(c=ug.test(c)?da(c):b(c));W(c)&&(c=new Date(c));if(!ga(c)||!isFinite(c.getTime()))return c;for(;d;)(l=vg.exec(d))?(h=bb(h,l,1),d=h.pop()):(h.push(d),d=null);var m=c.getTimezoneOffset();f&&(m=uc(f,m),c=Qb(c,f,!0));p(h,function(b){k=wg[b];g+=k?k(c,a.DATETIME_FORMATS, +m):"''"===b?"'":b.replace(/(^'|'$)/g,"").replace(/''/g,"'")});return g}}function ng(){return function(a,b){v(b)&&(b=2);return cb(a,b)}}function og(){return function(a,b,d){b=Infinity===Math.abs(Number(b))?Number(b):da(b);if(isNaN(b))return a;W(a)&&(a=a.toString());if(!L(a)&&!D(a))return a;d=!d||isNaN(d)?0:da(d);d=0>d?Math.max(0,a.length+d):d;return 0<=b?a.slice(d,d+b):0===d?a.slice(b,a.length):a.slice(Math.max(0,d+b),d)}}function zd(a){function b(b,d){d=d?-1:1;return b.map(function(b){var c=1,h=Za; +if(H(b))h=b;else if(D(b)){if("+"==b.charAt(0)||"-"==b.charAt(0))c="-"==b.charAt(0)?-1:1,b=b.substring(1);if(""!==b&&(h=a(b),h.constant))var k=h(),h=function(a){return a[k]}}return{get:h,descending:c*d}})}function d(a){switch(typeof a){case "number":case "boolean":case "string":return!0;default:return!1}}return function(a,e,f){if(null==a)return a;if(!Ca(a))throw O("orderBy")("notarray",a);L(e)||(e=[e]);0===e.length&&(e=["+"]);var g=b(e,f);g.push({get:function(){return{}},descending:f?-1:1});a=Array.prototype.map.call(a, +function(a,b){return{value:a,predicateValues:g.map(function(c){var e=c.get(a);c=typeof e;if(null===e)c="string",e="null";else if("string"===c)e=e.toLowerCase();else if("object"===c)a:{if("function"===typeof e.valueOf&&(e=e.valueOf(),d(e)))break a;if(qc(e)&&(e=e.toString(),d(e)))break a;e=b}return{value:e,type:c}})}});a.sort(function(a,b){for(var c=0,d=0,e=g.length;db||37<=b&&40>=b||m(a,this,this.value)});if(e.hasEvent("paste"))b.on("paste cut",m)}b.on("change",l);if(Hd[g]&&c.$$hasNativeValidators&&g===d.type)b.on("keydown wheel mousedown",function(a){if(!k){var b=this.validity,c=b.badInput,d=b.typeMismatch;k=f.defer(function(){k=null;b.badInput===c&&b.typeMismatch===d||l(a)})}});c.$render=function(){var a=c.$isEmpty(c.$viewValue)? +"":c.$viewValue;b.val()!==a&&b.val(a)}}function Lb(a,b){return function(d,c){var e,f;if(ga(d))return d;if(D(d)){'"'==d.charAt(0)&&'"'==d.charAt(d.length-1)&&(d=d.substring(1,d.length-1));if(xg.test(d))return new Date(d);a.lastIndex=0;if(e=a.exec(d))return e.shift(),f=c?{yyyy:c.getFullYear(),MM:c.getMonth()+1,dd:c.getDate(),HH:c.getHours(),mm:c.getMinutes(),ss:c.getSeconds(),sss:c.getMilliseconds()/1E3}:{yyyy:1970,MM:1,dd:1,HH:0,mm:0,ss:0,sss:0},p(e,function(a,c){c=x};g.$observe("min",function(a){x=y(a);h.$validate()})}if(A(g.max)||g.ngMax){var q;h.$validators.max=function(a){return!n(a)||v(q)||d(a)<=q};g.$observe("max",function(a){q=y(a);h.$validate()})}}}function Id(a,b,d,c){(c.$$hasNativeValidators=I(b[0].validity))&&c.$parsers.push(function(a){var c=b.prop("validity")||{};return c.badInput||c.typeMismatch?w:a})}function Jd(a,b,d,c,e){if(A(c)){a= +a(c);if(!a.constant)throw nb("constexpr",d,c);return a(b)}return e}function lc(a,b){a="ngClass"+a;return["$animate",function(d){function c(a,b){var c=[],d=0;a:for(;d(?:<\/\1>|)$/,Tb=/<|&#?\w+;/,Hf=/<([\w:-]+)/,If=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi, +ka={option:[1,'"],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ka.optgroup=ka.option;ka.tbody=ka.tfoot=ka.colgroup=ka.caption=ka.thead;ka.th=ka.td;var Pf=Node.prototype.contains||function(a){return!!(this.compareDocumentPosition(a)&16)},Pa=Y.prototype={ready:function(a){function b(){d||(d=!0,a())}var d=!1;"complete"=== +Q.readyState?setTimeout(b):(this.on("DOMContentLoaded",b),Y(N).on("load",b))},toString:function(){var a=[];p(this,function(b){a.push(""+b)});return"["+a.join(", ")+"]"},eq:function(a){return 0<=a?G(this[a]):G(this[this.length+a])},length:0,push:zg,sort:[].sort,splice:[].splice},Db={};p("multiple selected checked disabled readOnly required open".split(" "),function(a){Db[E(a)]=a});var Rc={};p("input select option textarea button form details".split(" "),function(a){Rc[a]=!0});var Yc={ngMinlength:"minlength", +ngMaxlength:"maxlength",ngMin:"min",ngMax:"max",ngPattern:"pattern"};p({data:Wb,removeData:fb,hasData:function(a){for(var b in gb[a.ng339])return!0;return!1},cleanData:function(a){for(var b=0,d=a.length;b/,Sf=/^[^\(]*\(\s*([^\)]*)\)/m,Ag=/,/,Bg=/^\s*(_?)(\S+?)\1\s*$/,Qf=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg,Ga=O("$injector");db.$$annotate=function(a,b,d){var c;if("function"===typeof a){if(!(c=a.$inject)){c=[];if(a.length){if(b)throw D(d)&&d||(d=a.name||Tf(a)),Ga("strictdi",d); +b=Sc(a);p(b[1].split(Ag),function(a){a.replace(Bg,function(a,b,d){c.push(d)})})}a.$inject=c}}else L(a)?(b=a.length-1,Qa(a[b],"fn"),c=a.slice(0,b)):Qa(a,"fn",!0);return c};var Nd=O("$animate"),We=function(){this.$get=z},Xe=function(){var a=new Sa,b=[];this.$get=["$$AnimateRunner","$rootScope",function(d,c){function e(a,b,c){var d=!1;b&&(b=D(b)?b.split(" "):L(b)?b:[],p(b,function(b){b&&(d=!0,a[b]=c)}));return d}function f(){p(b,function(b){var c=a.get(b);if(c){var d=Uf(b.attr("class")),e="",f="";p(c, +function(a,b){a!==!!d[b]&&(a?e+=(e.length?" ":"")+b:f+=(f.length?" ":"")+b)});p(b,function(a){e&&Bb(a,e);f&&Ab(a,f)});a.remove(b)}});b.length=0}return{enabled:z,on:z,off:z,pin:z,push:function(g,h,k,l){l&&l();k=k||{};k.from&&g.css(k.from);k.to&&g.css(k.to);if(k.addClass||k.removeClass)if(h=k.addClass,l=k.removeClass,k=a.get(g)||{},h=e(k,h,!0),l=e(k,l,!1),h||l)a.put(g,k),b.push(g),1===b.length&&c.$$postDigest(f);g=new d;g.complete();return g}}}]},Ue=["$provide",function(a){var b=this;this.$$registeredAnimations= +Object.create(null);this.register=function(d,c){if(d&&"."!==d.charAt(0))throw Nd("notcsel",d);var e=d+"-animation";b.$$registeredAnimations[d.substr(1)]=e;a.factory(e,c)};this.classNameFilter=function(a){if(1===arguments.length&&(this.$$classNameFilter=a instanceof RegExp?a:null)&&/(\s+|\/)ng-animate(\s+|\/)/.test(this.$$classNameFilter.toString()))throw Nd("nongcls","ng-animate");return this.$$classNameFilter};this.$get=["$$animateQueue",function(a){function b(a,c,d){if(d){var h;a:{for(h=0;h <= >= && || ! = |".split(" "),function(a){Mb[a]=!0});var Fg={n:"\n",f:"\f",r:"\r",t:"\t",v:"\v","'":"'",'"':'"'},fc=function(a){this.options= +a};fc.prototype={constructor:fc,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,b,d){d=d||this.index;b=A(b)?"s "+b+"-"+this.index+" ["+this.text.substring(b,d)+"]":" "+d;throw U("lexerr",a,b,this.text);},readNumber:function(){for(var a="",b=this.index;this.index","<=",">=");)a={type:r.BinaryExpression,operator:b.text,left:a,right:this.additive()};return a},additive:function(){for(var a=this.multiplicative(),b;b=this.expect("+","-");)a={type:r.BinaryExpression,operator:b.text,left:a,right:this.multiplicative()};return a},multiplicative:function(){for(var a=this.unary(),b;b=this.expect("*","/","%");)a={type:r.BinaryExpression,operator:b.text,left:a,right:this.unary()};return a},unary:function(){var a;return(a=this.expect("+", +"-","!"))?{type:r.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.selfReferential.hasOwnProperty(this.peek().text)?a=qa(this.selfReferential[this.consume().text]):this.options.literals.hasOwnProperty(this.peek().text)?a={type:r.Literal,value:this.options.literals[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 b;b=this.expect("(","[",".");)"("===b.text?(a={type:r.CallExpression,callee:a,arguments:this.parseArguments()},this.consume(")")):"["===b.text?(a={type:r.MemberExpression,object:a,property:this.expression(),computed:!0},this.consume("]")):"."===b.text?a={type:r.MemberExpression,object:a,property:this.identifier(),computed:!1}:this.throwError("IMPOSSIBLE");return a},filter:function(a){a= +[a];for(var b={type:r.CallExpression,callee:this.identifier(),arguments:a,filter:!0};this.expect(":");)a.push(this.expression());return b},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:r.Identifier,name:a.text}},constant:function(){return{type:r.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:r.ArrayExpression,elements:a}},object:function(){var a=[],b;if("}"!==this.peekToken().text){do{if(this.peek("}"))break;b={type:r.Property,kind:"init"};this.peek().constant?b.key=this.constant():this.peek().identifier?b.key=this.identifier():this.throwError("invalid key",this.peek());this.consume(":");b.value=this.expression();a.push(b)}while(this.expect(",")) +}this.consume("}");return{type:r.ObjectExpression,properties:a}},throwError:function(a,b){throw U("syntax",b.text,a,b.index+1,this.text,this.text.substring(b.index));},consume:function(a){if(0===this.tokens.length)throw U("ueoe",this.text);var b=this.expect(a);b||this.throwError("is unexpected, expecting ["+a+"]",this.peek());return b},peekToken:function(){if(0===this.tokens.length)throw U("ueoe",this.text);return this.tokens[0]},peek:function(a,b,d,c){return this.peekAhead(0,a,b,d,c)},peekAhead:function(a, +b,d,c,e){if(this.tokens.length>a){a=this.tokens[a];var f=a.text;if(f===b||f===d||f===c||f===e||!(b||d||c||e))return a}return!1},expect:function(a,b,d,c){return(a=this.peek(a,b,d,c))?(this.tokens.shift(),a):!1},selfReferential:{"this":{type:r.ThisExpression},$locals:{type:r.LocalsExpression}}};pd.prototype={compile:function(a,b){var d=this,c=this.astBuilder.ast(a);this.state={nextId:0,filters:{},expensiveChecks:b,fn:{vars:[],body:[],own:{}},assign:{vars:[],body:[],own:{}},inputs:[]};V(c,d.$filter); +var e="",f;this.stage="assign";if(f=nd(c))this.state.computing="assign",e=this.nextId(),this.recurse(f,e),this.return_(e),e="fn.assign="+this.generateFunction("assign","s,v,l");f=ld(c.body);d.stage="inputs";p(f,function(a,b){var c="fn"+b;d.state[c]={vars:[],body:[],own:{}};d.state.computing=c;var e=d.nextId();d.recurse(a,e);d.return_(e);d.state.inputs.push(c);a.watchId=b});this.state.computing="fn";this.stage="main";this.recurse(c);e='"'+this.USE+" "+this.STRICT+'";\n'+this.filterPrefix()+"var fn="+ +this.generateFunction("fn","s,l,a,i")+e+this.watchFns()+"return fn;";e=(new Function("$filter","ensureSafeMemberName","ensureSafeObject","ensureSafeFunction","getStringValue","ensureSafeAssignContext","ifDefined","plus","text",e))(this.$filter,Va,xa,jd,eg,Gb,ig,kd,a);this.state=this.stage=w;e.literal=od(c);e.constant=c.constant;return e},USE:"use",STRICT:"strict",watchFns:function(){var a=[],b=this.state.inputs,d=this;p(b,function(b){a.push("var "+b+"="+d.generateFunction(b,"s"))});b.length&&a.push("fn.inputs=["+ +b.join(",")+"];");return a.join("")},generateFunction:function(a,b){return"function("+b+"){"+this.varsPrefix(a)+this.body(a)+"};"},filterPrefix:function(){var a=[],b=this;p(this.state.filters,function(d,c){a.push(d+"=$filter("+b.escape(c)+")")});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,b,d,c,e,f){var g,h,k=this,l,m;c=c||z;if(!f&& +A(a.watchId))b=b||this.nextId(),this.if_("i",this.lazyAssign(b,this.computedMember("i",a.watchId)),this.lazyRecurse(a,b,d,c,e,!0));else switch(a.type){case r.Program:p(a.body,function(b,c){k.recurse(b.expression,w,w,function(a){h=a});c!==a.body.length-1?k.current().body.push(h,";"):k.return_(h)});break;case r.Literal:m=this.escape(a.value);this.assign(b,m);c(m);break;case r.UnaryExpression:this.recurse(a.argument,w,w,function(a){h=a});m=a.operator+"("+this.ifDefined(h,0)+")";this.assign(b,m);c(m); +break;case r.BinaryExpression:this.recurse(a.left,w,w,function(a){g=a});this.recurse(a.right,w,w,function(a){h=a});m="+"===a.operator?this.plus(g,h):"-"===a.operator?this.ifDefined(g,0)+a.operator+this.ifDefined(h,0):"("+g+")"+a.operator+"("+h+")";this.assign(b,m);c(m);break;case r.LogicalExpression:b=b||this.nextId();k.recurse(a.left,b);k.if_("&&"===a.operator?b:k.not(b),k.lazyRecurse(a.right,b));c(b);break;case r.ConditionalExpression:b=b||this.nextId();k.recurse(a.test,b);k.if_(b,k.lazyRecurse(a.alternate, +b),k.lazyRecurse(a.consequent,b));c(b);break;case r.Identifier:b=b||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);Va(a.name);k.if_("inputs"===k.stage||k.not(k.getHasOwnProperty("l",a.name)),function(){k.if_("inputs"===k.stage||"s",function(){e&&1!==e&&k.if_(k.not(k.nonComputedMember("s",a.name)),k.lazyAssign(k.nonComputedMember("s",a.name),"{}"));k.assign(b,k.nonComputedMember("s",a.name))})},b&& +k.lazyAssign(b,k.nonComputedMember("l",a.name)));(k.state.expensiveChecks||Hb(a.name))&&k.addEnsureSafeObject(b);c(b);break;case r.MemberExpression:g=d&&(d.context=this.nextId())||this.nextId();b=b||this.nextId();k.recurse(a.object,g,w,function(){k.if_(k.notNull(g),function(){e&&1!==e&&k.addEnsureSafeAssignContext(g);if(a.computed)h=k.nextId(),k.recurse(a.property,h),k.getStringValue(h),k.addEnsureSafeMemberName(h),e&&1!==e&&k.if_(k.not(k.computedMember(g,h)),k.lazyAssign(k.computedMember(g,h),"{}")), +m=k.ensureSafeObject(k.computedMember(g,h)),k.assign(b,m),d&&(d.computed=!0,d.name=h);else{Va(a.property.name);e&&1!==e&&k.if_(k.not(k.nonComputedMember(g,a.property.name)),k.lazyAssign(k.nonComputedMember(g,a.property.name),"{}"));m=k.nonComputedMember(g,a.property.name);if(k.state.expensiveChecks||Hb(a.property.name))m=k.ensureSafeObject(m);k.assign(b,m);d&&(d.computed=!1,d.name=a.property.name)}},function(){k.assign(b,"undefined")});c(b)},!!e);break;case r.CallExpression:b=b||this.nextId();a.filter? +(h=k.filter(a.callee.name),l=[],p(a.arguments,function(a){var b=k.nextId();k.recurse(a,b);l.push(b)}),m=h+"("+l.join(",")+")",k.assign(b,m),c(b)):(h=k.nextId(),g={},l=[],k.recurse(a.callee,h,g,function(){k.if_(k.notNull(h),function(){k.addEnsureSafeFunction(h);p(a.arguments,function(a){k.recurse(a,k.nextId(),w,function(a){l.push(k.ensureSafeObject(a))})});g.name?(k.state.expensiveChecks||k.addEnsureSafeObject(g.context),m=k.member(g.context,g.name,g.computed)+"("+l.join(",")+")"):m=h+"("+l.join(",")+ +")";m=k.ensureSafeObject(m);k.assign(b,m)},function(){k.assign(b,"undefined")});c(b)}));break;case r.AssignmentExpression:h=this.nextId();g={};if(!md(a.left))throw U("lval");this.recurse(a.left,w,g,function(){k.if_(k.notNull(g.context),function(){k.recurse(a.right,h);k.addEnsureSafeObject(k.member(g.context,g.name,g.computed));k.addEnsureSafeAssignContext(g.context);m=k.member(g.context,g.name,g.computed)+a.operator+h;k.assign(b,m);c(b||m)})},1);break;case r.ArrayExpression:l=[];p(a.elements,function(a){k.recurse(a, +k.nextId(),w,function(a){l.push(a)})});m="["+l.join(",")+"]";this.assign(b,m);c(m);break;case r.ObjectExpression:l=[];p(a.properties,function(a){k.recurse(a.value,k.nextId(),w,function(b){l.push(k.escape(a.key.type===r.Identifier?a.key.name:""+a.key.value)+":"+b)})});m="{"+l.join(",")+"}";this.assign(b,m);c(m);break;case r.ThisExpression:this.assign(b,"s");c("s");break;case r.LocalsExpression:this.assign(b,"l");c("l");break;case r.NGValueParameter:this.assign(b,"v"),c("v")}},getHasOwnProperty:function(a, +b){var d=a+"."+b,c=this.current().own;c.hasOwnProperty(d)||(c[d]=this.nextId(!1,a+"&&("+this.escape(b)+" in "+a+")"));return c[d]},assign:function(a,b){if(a)return this.current().body.push(a,"=",b,";"),a},filter:function(a){this.state.filters.hasOwnProperty(a)||(this.state.filters[a]=this.nextId(!0));return this.state.filters[a]},ifDefined:function(a,b){return"ifDefined("+a+","+this.escape(b)+")"},plus:function(a,b){return"plus("+a+","+b+")"},return_:function(a){this.current().body.push("return ", +a,";")},if_:function(a,b,d){if(!0===a)b();else{var c=this.current().body;c.push("if(",a,"){");b();c.push("}");d&&(c.push("else{"),d(),c.push("}"))}},not:function(a){return"!("+a+")"},notNull:function(a){return a+"!=null"},nonComputedMember:function(a,b){return a+"."+b},computedMember:function(a,b){return a+"["+b+"]"},member:function(a,b,d){return d?this.computedMember(a,b):this.nonComputedMember(a,b)},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),";")},addEnsureSafeAssignContext:function(a){this.current().body.push(this.ensureSafeAssignContext(a),";")},ensureSafeObject:function(a){return"ensureSafeObject("+a+",text)"},ensureSafeMemberName:function(a){return"ensureSafeMemberName("+a+",text)"},ensureSafeFunction:function(a){return"ensureSafeFunction("+a+",text)"},getStringValue:function(a){this.assign(a,"getStringValue("+a+")")},ensureSafeAssignContext:function(a){return"ensureSafeAssignContext("+ +a+",text)"},lazyRecurse:function(a,b,d,c,e,f){var g=this;return function(){g.recurse(a,b,d,c,e,f)}},lazyAssign:function(a,b){var d=this;return function(){d.assign(a,b)}},stringEscapeRegex:/[^ a-zA-Z0-9]/g,stringEscapeFn:function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)},escape:function(a){if(D(a))return"'"+a.replace(this.stringEscapeRegex,this.stringEscapeFn)+"'";if(W(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 U("esc");},nextId:function(a,b){var d="v"+this.state.nextId++;a||this.current().vars.push(d+(b?"="+b:""));return d},current:function(){return this.state[this.state.computing]}};qd.prototype={compile:function(a,b){var d=this,c=this.astBuilder.ast(a);this.expression=a;this.expensiveChecks=b;V(c,d.$filter);var e,f;if(e=nd(c))f=this.recurse(e);e=ld(c.body);var g;e&&(g=[],p(e,function(a,b){var c=d.recurse(a);a.input=c;g.push(c);a.watchId=b}));var h=[];p(c.body,function(a){h.push(d.recurse(a.expression))}); +e=0===c.body.length?z:1===c.body.length?h[0]:function(a,b){var c;p(h,function(d){c=d(a,b)});return c};f&&(e.assign=function(a,b,c){return f(a,c,b)});g&&(e.inputs=g);e.literal=od(c);e.constant=c.constant;return e},recurse:function(a,b,d){var c,e,f=this,g;if(a.input)return this.inputs(a.input,a.watchId);switch(a.type){case r.Literal:return this.value(a.value,b);case r.UnaryExpression:return e=this.recurse(a.argument),this["unary"+a.operator](e,b);case r.BinaryExpression:return c=this.recurse(a.left), +e=this.recurse(a.right),this["binary"+a.operator](c,e,b);case r.LogicalExpression:return c=this.recurse(a.left),e=this.recurse(a.right),this["binary"+a.operator](c,e,b);case r.ConditionalExpression:return this["ternary?:"](this.recurse(a.test),this.recurse(a.alternate),this.recurse(a.consequent),b);case r.Identifier:return Va(a.name,f.expression),f.identifier(a.name,f.expensiveChecks||Hb(a.name),b,d,f.expression);case r.MemberExpression:return c=this.recurse(a.object,!1,!!d),a.computed||(Va(a.property.name, +f.expression),e=a.property.name),a.computed&&(e=this.recurse(a.property)),a.computed?this.computedMember(c,e,b,d,f.expression):this.nonComputedMember(c,e,f.expensiveChecks,b,d,f.expression);case r.CallExpression:return g=[],p(a.arguments,function(a){g.push(f.recurse(a))}),a.filter&&(e=this.$filter(a.callee.name)),a.filter||(e=this.recurse(a.callee,!0)),a.filter?function(a,c,d,f){for(var n=[],p=0;p":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)>b(c,e,f,g);return d?{value:c}:c}},"binary<=":function(a,b,d){return function(c,e,f,g){c=a(c,e,f, +g)<=b(c,e,f,g);return d?{value:c}:c}},"binary>=":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)>=b(c,e,f,g);return d?{value:c}:c}},"binary&&":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)&&b(c,e,f,g);return d?{value:c}:c}},"binary||":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)||b(c,e,f,g);return d?{value:c}:c}},"ternary?:":function(a,b,d,c){return function(e,f,g,h){e=a(e,f,g,h)?b(e,f,g,h):d(e,f,g,h);return c?{value:e}:e}},value:function(a,b){return function(){return b?{context:w, +name:w,value:a}:a}},identifier:function(a,b,d,c,e){return function(f,g,h,k){f=g&&a in g?g:f;c&&1!==c&&f&&!f[a]&&(f[a]={});g=f?f[a]:w;b&&xa(g,e);return d?{context:f,name:a,value:g}:g}},computedMember:function(a,b,d,c,e){return function(f,g,h,k){var l=a(f,g,h,k),m,n;null!=l&&(m=b(f,g,h,k),m+="",Va(m,e),c&&1!==c&&(Gb(l),l&&!l[m]&&(l[m]={})),n=l[m],xa(n,e));return d?{context:l,name:m,value:n}:n}},nonComputedMember:function(a,b,d,c,e,f){return function(g,h,k,l){g=a(g,h,k,l);e&&1!==e&&(Gb(g),g&&!g[b]&& +(g[b]={}));h=null!=g?g[b]:w;(d||Hb(b))&&xa(h,f);return c?{context:g,name:b,value:h}:h}},inputs:function(a,b){return function(d,c,e,f){return f?f[b]:a(d,c,e)}}};var gc=function(a,b,d){this.lexer=a;this.$filter=b;this.options=d;this.ast=new r(a,d);this.astCompiler=d.csp?new qd(this.ast,b):new pd(this.ast,b)};gc.prototype={constructor:gc,parse:function(a){return this.astCompiler.compile(a,this.options.expensiveChecks)}};var jg=Object.prototype.valueOf,Ba=O("$sce"),fa={HTML:"html",CSS:"css",URL:"url", +RESOURCE_URL:"resourceUrl",JS:"js"},lg=O("$compile"),aa=Q.createElement("a"),ud=wa(N.location.href);vd.$inject=["$document"];Ic.$inject=["$provide"];var Cd=22,Bd=".",ic="0";wd.$inject=["$locale"];yd.$inject=["$locale"];var wg={yyyy:ba("FullYear",4,0,!1,!0),yy:ba("FullYear",2,0,!0,!0),y:ba("FullYear",1,0,!1,!0),MMMM:kb("Month"),MMM:kb("Month",!0),MM:ba("Month",2,1),M:ba("Month",1,1),LLLL:kb("Month",!1,!0),dd:ba("Date",2),d:ba("Date",1),HH:ba("Hours",2),H:ba("Hours",1),hh:ba("Hours",2,-12),h:ba("Hours", +1,-12),mm:ba("Minutes",2),m:ba("Minutes",1),ss:ba("Seconds",2),s:ba("Seconds",1),sss:ba("Milliseconds",3),EEEE:kb("Day"),EEE:kb("Day",!0),a:function(a,b){return 12>a.getHours()?b.AMPMS[0]:b.AMPMS[1]},Z:function(a,b,d){a=-1*d;return a=(0<=a?"+":"")+(Ib(Math[0=a.getFullYear()?b.ERANAMES[0]:b.ERANAMES[1]}},vg=/((?:[^yMLdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|L+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/, +ug=/^\-?\d+$/;xd.$inject=["$locale"];var pg=ia(E),qg=ia(ub);zd.$inject=["$parse"];var ke=ia({restrict:"E",compile:function(a,b){if(!b.href&&!b.xlinkHref)return function(a,b){if("a"===b[0].nodeName.toLowerCase()){var e="[object SVGAnimatedString]"===ha.call(b.prop("href"))?"xlink:href":"href";b.on("click",function(a){b.attr(e)||a.preventDefault()})}}}}),vb={};p(Db,function(a,b){function d(a,d,e){a.$watch(e[c],function(a){e.$set(b,!!a)})}if("multiple"!=a){var c=ua("ng-"+b),e=d;"checked"===a&&(e=function(a, +b,e){e.ngModel!==e[c]&&d(a,b,e)});vb[c]=function(){return{restrict:"A",priority:100,link:e}}}});p(Yc,function(a,b){vb[b]=function(){return{priority:100,link:function(a,c,e){if("ngPattern"===b&&"/"==e.ngPattern.charAt(0)&&(c=e.ngPattern.match(yg))){e.$set("ngPattern",new RegExp(c[1],c[2]));return}a.$watch(e[b],function(a){e.$set(b,a)})}}}});p(["src","srcset","href"],function(a){var b=ua("ng-"+a);vb[b]=function(){return{priority:99,link:function(d,c,e){var f=a,g=a;"href"===a&&"[object SVGAnimatedString]"=== +ha.call(c.prop("href"))&&(g="xlinkHref",e.$attr[g]="xlink:href",f=null);e.$observe(b,function(b){b?(e.$set(g,b),za&&f&&c.prop(f,e[g])):"href"===a&&e.$set(g,null)})}}}});var Jb={$addControl:z,$$renameControl:function(a,b){a.$name=b},$removeControl:z,$setValidity:z,$setDirty:z,$setPristine:z,$setSubmitted:z};Fd.$inject=["$element","$attrs","$scope","$animate","$interpolate"];var Od=function(a){return["$timeout","$parse",function(b,d){function c(a){return""===a?d('this[""]').assign:d(a).assign||z}return{name:"form", +restrict:a?"EAC":"E",require:["form","^^?form"],controller:Fd,compile:function(d,f){d.addClass(Wa).addClass(ob);var g=f.name?"name":a&&f.ngForm?"ngForm":!1;return{pre:function(a,d,e,f){var n=f[0];if(!("action"in e)){var p=function(b){a.$apply(function(){n.$commitViewValue();n.$setSubmitted()});b.preventDefault()};d[0].addEventListener("submit",p,!1);d.on("$destroy",function(){b(function(){d[0].removeEventListener("submit",p,!1)},0,!1)})}(f[1]||n.$$parentForm).$addControl(n);var r=g?c(n.$name):z;g&& +(r(a,n),e.$observe(g,function(b){n.$name!==b&&(r(a,w),n.$$parentForm.$$renameControl(n,b),r=c(n.$name),r(a,n))}));d.on("$destroy",function(){n.$$parentForm.$removeControl(n);r(a,w);S(n,Jb)})}}}}}]},le=Od(),ye=Od(!0),xg=/^\d{4,}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+(?:[+-][0-2]\d:[0-5]\d|Z)$/,Gg=/^[a-z][a-z\d.+-]*:\/*(?:[^:@]+(?::[^@]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+\])(?::\d+)?(?:\/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$/i,Hg=/^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i, +Ig=/^\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})?)?$/,mc=/^(\d{4,})-W(\d\d)$/,Rd=/^(\d{4,})-(\d\d)$/,Sd=/^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/,Hd=X();p(["date","datetime-local","month","time","week"],function(a){Hd[a]=!0});var Td={text:function(a,b,d,c,e,f){lb(a,b,d,c,e,f);kc(c)},date:mb("date",Pd,Lb(Pd,["yyyy","MM","dd"]),"yyyy-MM-dd"),"datetime-local":mb("datetimelocal",Qd,Lb(Qd,"yyyy MM dd HH mm ss sss".split(" ")), +"yyyy-MM-ddTHH:mm:ss.sss"),time:mb("time",Sd,Lb(Sd,["HH","mm","ss","sss"]),"HH:mm:ss.sss"),week:mb("week",mc,function(a,b){if(ga(a))return a;if(D(a)){mc.lastIndex=0;var d=mc.exec(a);if(d){var c=+d[1],e=+d[2],f=d=0,g=0,h=0,k=Dd(c),e=7*(e-1);b&&(d=b.getHours(),f=b.getMinutes(),g=b.getSeconds(),h=b.getMilliseconds());return new Date(c,0,k.getDate()+e,d,f,g,h)}}return NaN},"yyyy-Www"),month:mb("month",Rd,Lb(Rd,["yyyy","MM"]),"yyyy-MM"),number:function(a,b,d,c,e,f){Id(a,b,d,c);lb(a,b,d,c,e,f);c.$$parserName= +"number";c.$parsers.push(function(a){return c.$isEmpty(a)?null:Ig.test(a)?parseFloat(a):w});c.$formatters.push(function(a){if(!c.$isEmpty(a)){if(!W(a))throw nb("numfmt",a);a=a.toString()}return a});if(A(d.min)||d.ngMin){var g;c.$validators.min=function(a){return c.$isEmpty(a)||v(g)||a>=g};d.$observe("min",function(a){A(a)&&!W(a)&&(a=parseFloat(a,10));g=W(a)&&!isNaN(a)?a:w;c.$validate()})}if(A(d.max)||d.ngMax){var h;c.$validators.max=function(a){return c.$isEmpty(a)||v(h)||a<=h};d.$observe("max",function(a){A(a)&& +!W(a)&&(a=parseFloat(a,10));h=W(a)&&!isNaN(a)?a:w;c.$validate()})}},url:function(a,b,d,c,e,f){lb(a,b,d,c,e,f);kc(c);c.$$parserName="url";c.$validators.url=function(a,b){var d=a||b;return c.$isEmpty(d)||Gg.test(d)}},email:function(a,b,d,c,e,f){lb(a,b,d,c,e,f);kc(c);c.$$parserName="email";c.$validators.email=function(a,b){var d=a||b;return c.$isEmpty(d)||Hg.test(d)}},radio:function(a,b,d,c){v(d.name)&&b.attr("name",++pb);b.on("click",function(a){b[0].checked&&c.$setViewValue(d.value,a&&a.type)});c.$render= +function(){b[0].checked=d.value==c.$viewValue};d.$observe("value",c.$render)},checkbox:function(a,b,d,c,e,f,g,h){var k=Jd(h,a,"ngTrueValue",d.ngTrueValue,!0),l=Jd(h,a,"ngFalseValue",d.ngFalseValue,!1);b.on("click",function(a){c.$setViewValue(b[0].checked,a&&a.type)});c.$render=function(){b[0].checked=c.$viewValue};c.$isEmpty=function(a){return!1===a};c.$formatters.push(function(a){return oa(a,k)});c.$parsers.push(function(a){return a?k:l})},hidden:z,button:z,submit:z,reset:z,file:z},Cc=["$browser", +"$sniffer","$filter","$parse",function(a,b,d,c){return{restrict:"E",require:["?ngModel"],link:{pre:function(e,f,g,h){h[0]&&(Td[E(g.type)]||Td.text)(e,f,g,h[0],b,a,d,c)}}}}],Jg=/^(true|false|\d+)$/,Qe=function(){return{restrict:"A",priority:100,compile:function(a,b){return Jg.test(b.ngValue)?function(a,b,e){e.$set("value",a.$eval(e.ngValue))}:function(a,b,e){a.$watch(e.ngValue,function(a){e.$set("value",a)})}}}},qe=["$compile",function(a){return{restrict:"AC",compile:function(b){a.$$addBindingClass(b); +return function(b,c,e){a.$$addBindingInfo(c,e.ngBind);c=c[0];b.$watch(e.ngBind,function(a){c.textContent=v(a)?"":a})}}}}],se=["$interpolate","$compile",function(a,b){return{compile:function(d){b.$$addBindingClass(d);return function(c,d,f){c=a(d.attr(f.$attr.ngBindTemplate));b.$$addBindingInfo(d,c.expressions);d=d[0];f.$observe("ngBindTemplate",function(a){d.textContent=v(a)?"":a})}}}}],re=["$sce","$parse","$compile",function(a,b,d){return{restrict:"A",compile:function(c,e){var f=b(e.ngBindHtml),g= +b(e.ngBindHtml,function(a){return(a||"").toString()});d.$$addBindingClass(c);return function(b,c,e){d.$$addBindingInfo(c,e.ngBindHtml);b.$watch(g,function(){c.html(a.getTrustedHtml(f(b))||"")})}}}}],Pe=ia({restrict:"A",require:"ngModel",link:function(a,b,d,c){c.$viewChangeListeners.push(function(){a.$eval(d.ngChange)})}}),te=lc("",!0),ve=lc("Odd",0),ue=lc("Even",1),we=La({compile:function(a,b){b.$set("ngCloak",w);a.removeClass("ng-cloak")}}),xe=[function(){return{restrict:"A",scope:!0,controller:"@", +priority:500}}],Hc={},Kg={blur:!0,focus:!0};p("click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste".split(" "),function(a){var b=ua("ng-"+a);Hc[b]=["$parse","$rootScope",function(d,c){return{restrict:"A",compile:function(e,f){var g=d(f[b],null,!0);return function(b,d){d.on(a,function(d){var e=function(){g(b,{$event:d})};Kg[a]&&c.$$phase?b.$evalAsync(e):b.$apply(e)})}}}}]});var Ae=["$animate","$compile",function(a, +b){return{multiElement:!0,transclude:"element",priority:600,terminal:!0,restrict:"A",$$tlb:!0,link:function(d,c,e,f,g){var h,k,l;d.$watch(e.ngIf,function(d){d?k||g(function(d,f){k=f;d[d.length++]=b.$$createComment("end ngIf",e.ngIf);h={clone:d};a.enter(d,c.parent(),c)}):(l&&(l.remove(),l=null),k&&(k.$destroy(),k=null),h&&(l=tb(h.clone),a.leave(l).then(function(){l=null}),h=null))})}}}],Be=["$templateRequest","$anchorScroll","$animate",function(a,b,d){return{restrict:"ECA",priority:400,terminal:!0, +transclude:"element",controller:ea.noop,compile:function(c,e){var f=e.ngInclude||e.src,g=e.onload||"",h=e.autoscroll;return function(c,e,m,n,p){var r=0,w,x,q,t=function(){x&&(x.remove(),x=null);w&&(w.$destroy(),w=null);q&&(d.leave(q).then(function(){x=null}),x=q,q=null)};c.$watch(f,function(f){var m=function(){!A(h)||h&&!c.$eval(h)||b()},x=++r;f?(a(f,!0).then(function(a){if(!c.$$destroyed&&x===r){var b=c.$new();n.template=a;a=p(b,function(a){t();d.enter(a,null,e).then(m)});w=b;q=a;w.$emit("$includeContentLoaded", +f);c.$eval(g)}},function(){c.$$destroyed||x!==r||(t(),c.$emit("$includeContentError",f))}),c.$emit("$includeContentRequested",f)):(t(),n.template=null)})}}}}],Se=["$compile",function(a){return{restrict:"ECA",priority:-400,require:"ngInclude",link:function(b,d,c,e){ha.call(d[0]).match(/SVG/)?(d.empty(),a(Kc(e.template,Q).childNodes)(b,function(a){d.append(a)},{futureParentElement:d})):(d.html(e.template),a(d.contents())(b))}}}],Ce=La({priority:450,compile:function(){return{pre:function(a,b,d){a.$eval(d.ngInit)}}}}), +Oe=function(){return{restrict:"A",priority:100,require:"ngModel",link:function(a,b,d,c){var e=b.attr(d.$attr.ngList)||", ",f="false"!==d.ngTrim,g=f?Z(e):e;c.$parsers.push(function(a){if(!v(a)){var b=[];a&&p(a.split(g),function(a){a&&b.push(f?Z(a):a)});return b}});c.$formatters.push(function(a){return L(a)?a.join(e):w});c.$isEmpty=function(a){return!a||!a.length}}}},ob="ng-valid",Kd="ng-invalid",Wa="ng-pristine",Kb="ng-dirty",Md="ng-pending",nb=O("ngModel"),Lg=["$scope","$exceptionHandler","$attrs", +"$element","$parse","$animate","$timeout","$rootScope","$q","$interpolate",function(a,b,d,c,e,f,g,h,k,l){this.$modelValue=this.$viewValue=Number.NaN;this.$$rawModelValue=w;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=w;this.$name=l(d.name||"",!1)(a);this.$$parentForm=Jb;var m=e(d.ngModel), +n=m.assign,r=m,B=n,K=null,x,q=this;this.$$setOptions=function(a){if((q.$options=a)&&a.getterSetter){var b=e(d.ngModel+"()"),f=e(d.ngModel+"($$$p)");r=function(a){var c=m(a);H(c)&&(c=b(a));return c};B=function(a,b){H(m(a))?f(a,{$$$p:b}):n(a,b)}}else if(!m.assign)throw nb("nonassign",d.ngModel,ta(c));};this.$render=z;this.$isEmpty=function(a){return v(a)||""===a||null===a||a!==a};this.$$updateEmptyClasses=function(a){q.$isEmpty(a)?(f.removeClass(c,"ng-not-empty"),f.addClass(c,"ng-empty")):(f.removeClass(c, +"ng-empty"),f.addClass(c,"ng-not-empty"))};var t=0;Gd({ctrl:this,$element:c,set:function(a,b){a[b]=!0},unset:function(a,b){delete a[b]},$animate:f});this.$setPristine=function(){q.$dirty=!1;q.$pristine=!0;f.removeClass(c,Kb);f.addClass(c,Wa)};this.$setDirty=function(){q.$dirty=!0;q.$pristine=!1;f.removeClass(c,Wa);f.addClass(c,Kb);q.$$parentForm.$setDirty()};this.$setUntouched=function(){q.$touched=!1;q.$untouched=!0;f.setClass(c,"ng-untouched","ng-touched")};this.$setTouched=function(){q.$touched= +!0;q.$untouched=!1;f.setClass(c,"ng-touched","ng-untouched")};this.$rollbackViewValue=function(){g.cancel(K);q.$viewValue=q.$$lastCommittedViewValue;q.$render()};this.$validate=function(){if(!W(q.$modelValue)||!isNaN(q.$modelValue)){var a=q.$$rawModelValue,b=q.$valid,c=q.$modelValue,d=q.$options&&q.$options.allowInvalid;q.$$runValidators(a,q.$$lastCommittedViewValue,function(e){d||b===e||(q.$modelValue=e?a:w,q.$modelValue!==c&&q.$$writeModelToScope())})}};this.$$runValidators=function(a,b,c){function d(){var c= +!0;p(q.$validators,function(d,e){var g=d(a,b);c=c&&g;f(e,g)});return c?!0:(p(q.$asyncValidators,function(a,b){f(b,null)}),!1)}function e(){var c=[],d=!0;p(q.$asyncValidators,function(e,g){var h=e(a,b);if(!h||!H(h.then))throw nb("nopromise",h);f(g,w);c.push(h.then(function(){f(g,!0)},function(){d=!1;f(g,!1)}))});c.length?k.all(c).then(function(){g(d)},z):g(!0)}function f(a,b){h===t&&q.$setValidity(a,b)}function g(a){h===t&&c(a)}t++;var h=t;(function(){var a=q.$$parserName||"parse";if(v(x))f(a,null); +else return x||(p(q.$validators,function(a,b){f(b,null)}),p(q.$asyncValidators,function(a,b){f(b,null)})),f(a,x),x;return!0})()?d()?e():g(!1):g(!1)};this.$commitViewValue=function(){var a=q.$viewValue;g.cancel(K);if(q.$$lastCommittedViewValue!==a||""===a&&q.$$hasNativeValidators)q.$$updateEmptyClasses(a),q.$$lastCommittedViewValue=a,q.$pristine&&this.$setDirty(),this.$$parseAndValidate()};this.$$parseAndValidate=function(){var b=q.$$lastCommittedViewValue;if(x=v(b)?w:!0)for(var c=0;ce||c.$isEmpty(b)|| +b.length<=e}}}}},Fc=function(){return{restrict:"A",require:"?ngModel",link:function(a,b,d,c){if(c){var e=0;d.$observe("minlength",function(a){e=da(a)||0;c.$validate()});c.$validators.minlength=function(a,b){return c.$isEmpty(b)||b.length>=e}}}}};N.angular.bootstrap?N.console&&console.log("WARNING: Tried to load angular more than once."):(fe(),he(ea),ea.module("ngLocale",[],["$provide",function(a){function b(a){a+="";var b=a.indexOf(".");return-1==b?0:a.length-b-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(" "),STANDALONEMONTH:"January February March April May June July August September October November December".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",localeID:"en_US",pluralCat:function(a, +c){var e=a|0,f=c;w===f&&(f=Math.min(b(a),3));Math.pow(10,f);return 1==e&&0==f?"one":"other"}})}]),G(Q).ready(function(){be(Q,xc)}))})(window,document);!window.angular.$$csp().noInlineStyle&&window.angular.element(document.head).prepend(''); +//# sourceMappingURL=angular.min.js.map diff --git a/public/js/index.js b/public/js/index.js new file mode 100644 index 000000000..5c0c9324d --- /dev/null +++ b/public/js/index.js @@ -0,0 +1 @@ +!function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a="function"==typeof require&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}for(var i="function"==typeof require&&require,o=0;oi;i++)row=result[i],results.push(row.attributes);return results}(),$scope.loaded=!0,$scope.$apply()})}},{"../../parse/class/Meetup":4}],3:[function(require,module,exports){var Mentor;Mentor=require("../../parse/class/Mentor"),module.exports=function($scope,$http){return $scope.loaded=!1,$scope.mentors=[],new Parse.Query(Mentor).ascending("order").find().then(function(result){var row;return $scope.mentors=function(){var i,len,results;for(results=[],i=0,len=result.length;len>i;i++)row=result[i],results.push(row.attributes);return results}(),$scope.loaded=!0,$scope.$apply()})}},{"../../parse/class/Mentor":5}],4:[function(require,module,exports){module.exports=Parse.Object.extend("Meetup",{initialize:function(attrs){return attrs&&this.setValues(attrs),this},setValues:function(attrs){var ref,ref1,ref2;return(null!=attrs.source||null!=attrs.id)&&this.set("source",attrs.source||attrs.id),null!=attrs.name&&this.set("name",attrs.name),null!=attrs.location&&this.set("location",attrs.location),((null!=(ref=attrs.venue)?ref.id:void 0)||attrs.venue_id)&&this.set("venue_id",(null!=(ref1=attrs.venue)?ref1.id:void 0)||attrs.venue_id),null!=attrs.description&&this.set("description",attrs.description),null!=attrs.start_time&&("string"==typeof attrs.start_time?this.set("start_time",new Date(attrs.start_time)):this.set("start_time",attrs.start_time)),null!=attrs.end_time&&("string"==typeof attrs.end_time?this.set("end_time",new Date(attrs.end_time)):this.set("end_time",attrs.end_time)),null!=(null!=(ref2=attrs.cover)?ref2.source:void 0)&&this.set("cover_source",attrs.cover.source),this}})},{}],5:[function(require,module,exports){module.exports=Parse.Object.extend("Mentor",{initialize:function(attrs){return attrs&&this.setValues(attrs),this},setValues:function(attrs){return(null!=attrs.source||null!=attrs.id)&&this.set("source",attrs.source||attrs.id),null!=attrs.last_name&&this.set("last_name",attrs.last_name),null!=attrs.first_name&&this.set("first_name",attrs.first_name),null!=attrs.description&&this.set("description",attrs.description),null!=attrs.last_attended&&("string"==typeof attrs.last_attended?this.set("last_attended",new Date(attrs.last_attended)):this.set("last_attended",attrs.last_attended)),this}})},{}]},{},[1]); diff --git a/public/js/parse.js b/public/js/parse.js new file mode 100644 index 000000000..70edf5939 --- /dev/null +++ b/public/js/parse.js @@ -0,0 +1,9629 @@ +/*! + * Parse JavaScript SDK + * Version: 1.5.0 + * Built: Fri Jul 10 2015 17:05:46 + * http://parse.com + * + * Copyright 2015 Parse, LLC + * + * Includes: Underscore.js + * Copyright 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. + * Released under the MIT license. + */ +(function(root) { + root.Parse = root.Parse || {}; + root.Parse.VERSION = "js1.5.0"; +}(this)); +// Underscore.js 1.4.4 +// http://underscorejs.org +// (c) 2009-2013 Jeremy Ashkenas, DocumentCloud Inc. +// Underscore may be freely distributed under the MIT license. + +(function() { + + // Baseline setup + // -------------- + + // Establish the root object, `window` in the browser, or `global` on the server. + var root = this; + + // Save the previous value of the `_` variable. + var previousUnderscore = root._; + + // Establish the object that gets returned to break out of a loop iteration. + var breaker = {}; + + // Save bytes in the minified (but not gzipped) version: + var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; + + // Create quick reference variables for speed access to core prototypes. + var push = ArrayProto.push, + slice = ArrayProto.slice, + concat = ArrayProto.concat, + toString = ObjProto.toString, + hasOwnProperty = ObjProto.hasOwnProperty; + + // All **ECMAScript 5** native function implementations that we hope to use + // are declared here. + var + nativeForEach = ArrayProto.forEach, + nativeMap = ArrayProto.map, + nativeReduce = ArrayProto.reduce, + nativeReduceRight = ArrayProto.reduceRight, + nativeFilter = ArrayProto.filter, + nativeEvery = ArrayProto.every, + nativeSome = ArrayProto.some, + nativeIndexOf = ArrayProto.indexOf, + nativeLastIndexOf = ArrayProto.lastIndexOf, + nativeIsArray = Array.isArray, + nativeKeys = Object.keys, + nativeBind = FuncProto.bind; + + // Create a safe reference to the Underscore object for use below. + var _ = function(obj) { + if (obj instanceof _) return obj; + if (!(this instanceof _)) return new _(obj); + this._wrapped = obj; + }; + + // Export the Underscore object for **Node.js**, with + // backwards-compatibility for the old `require()` API. If we're in + // the browser, add `_` as a global object via a string identifier, + // for Closure Compiler "advanced" mode. + if (typeof exports !== 'undefined') { + if (typeof module !== 'undefined' && module.exports) { + exports = module.exports = _; + } + exports._ = _; + } else { + root._ = _; + } + + // Current version. + _.VERSION = '1.4.4'; + + // Collection Functions + // -------------------- + + // The cornerstone, an `each` implementation, aka `forEach`. + // Handles objects with the built-in `forEach`, arrays, and raw objects. + // Delegates to **ECMAScript 5**'s native `forEach` if available. + var each = _.each = _.forEach = function(obj, iterator, context) { + if (obj == null) return; + if (nativeForEach && obj.forEach === nativeForEach) { + obj.forEach(iterator, context); + } else if (obj.length === +obj.length) { + for (var i = 0, l = obj.length; i < l; i++) { + if (iterator.call(context, obj[i], i, obj) === breaker) return; + } + } else { + for (var key in obj) { + if (_.has(obj, key)) { + if (iterator.call(context, obj[key], key, obj) === breaker) return; + } + } + } + }; + + // Return the results of applying the iterator to each element. + // Delegates to **ECMAScript 5**'s native `map` if available. + _.map = _.collect = function(obj, iterator, context) { + var results = []; + if (obj == null) return results; + if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context); + each(obj, function(value, index, list) { + results[results.length] = iterator.call(context, value, index, list); + }); + return results; + }; + + var reduceError = 'Reduce of empty array with no initial value'; + + // **Reduce** builds up a single result from a list of values, aka `inject`, + // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available. + _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) { + var initial = arguments.length > 2; + if (obj == null) obj = []; + if (nativeReduce && obj.reduce === nativeReduce) { + if (context) iterator = _.bind(iterator, context); + return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator); + } + each(obj, function(value, index, list) { + if (!initial) { + memo = value; + initial = true; + } else { + memo = iterator.call(context, memo, value, index, list); + } + }); + if (!initial) throw new TypeError(reduceError); + return memo; + }; + + // The right-associative version of reduce, also known as `foldr`. + // Delegates to **ECMAScript 5**'s native `reduceRight` if available. + _.reduceRight = _.foldr = function(obj, iterator, memo, context) { + var initial = arguments.length > 2; + if (obj == null) obj = []; + if (nativeReduceRight && obj.reduceRight === nativeReduceRight) { + if (context) iterator = _.bind(iterator, context); + return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator); + } + var length = obj.length; + if (length !== +length) { + var keys = _.keys(obj); + length = keys.length; + } + each(obj, function(value, index, list) { + index = keys ? keys[--length] : --length; + if (!initial) { + memo = obj[index]; + initial = true; + } else { + memo = iterator.call(context, memo, obj[index], index, list); + } + }); + if (!initial) throw new TypeError(reduceError); + return memo; + }; + + // Return the first value which passes a truth test. Aliased as `detect`. + _.find = _.detect = function(obj, iterator, context) { + var result; + any(obj, function(value, index, list) { + if (iterator.call(context, value, index, list)) { + result = value; + return true; + } + }); + return result; + }; + + // Return all the elements that pass a truth test. + // Delegates to **ECMAScript 5**'s native `filter` if available. + // Aliased as `select`. + _.filter = _.select = function(obj, iterator, context) { + var results = []; + if (obj == null) return results; + if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context); + each(obj, function(value, index, list) { + if (iterator.call(context, value, index, list)) results[results.length] = value; + }); + return results; + }; + + // Return all the elements for which a truth test fails. + _.reject = function(obj, iterator, context) { + return _.filter(obj, function(value, index, list) { + return !iterator.call(context, value, index, list); + }, context); + }; + + // Determine whether all of the elements match a truth test. + // Delegates to **ECMAScript 5**'s native `every` if available. + // Aliased as `all`. + _.every = _.all = function(obj, iterator, context) { + iterator || (iterator = _.identity); + var result = true; + if (obj == null) return result; + if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context); + each(obj, function(value, index, list) { + if (!(result = result && iterator.call(context, value, index, list))) return breaker; + }); + return !!result; + }; + + // Determine if at least one element in the object matches a truth test. + // Delegates to **ECMAScript 5**'s native `some` if available. + // Aliased as `any`. + var any = _.some = _.any = function(obj, iterator, context) { + iterator || (iterator = _.identity); + var result = false; + if (obj == null) return result; + if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context); + each(obj, function(value, index, list) { + if (result || (result = iterator.call(context, value, index, list))) return breaker; + }); + return !!result; + }; + + // Determine if the array or object contains a given value (using `===`). + // Aliased as `include`. + _.contains = _.include = function(obj, target) { + if (obj == null) return false; + if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; + return any(obj, function(value) { + return value === target; + }); + }; + + // Invoke a method (with arguments) on every item in a collection. + _.invoke = function(obj, method) { + var args = slice.call(arguments, 2); + var isFunc = _.isFunction(method); + return _.map(obj, function(value) { + return (isFunc ? method : value[method]).apply(value, args); + }); + }; + + // Convenience version of a common use case of `map`: fetching a property. + _.pluck = function(obj, key) { + return _.map(obj, function(value){ return value[key]; }); + }; + + // Convenience version of a common use case of `filter`: selecting only objects + // containing specific `key:value` pairs. + _.where = function(obj, attrs, first) { + if (_.isEmpty(attrs)) return first ? null : []; + return _[first ? 'find' : 'filter'](obj, function(value) { + for (var key in attrs) { + if (attrs[key] !== value[key]) return false; + } + return true; + }); + }; + + // Convenience version of a common use case of `find`: getting the first object + // containing specific `key:value` pairs. + _.findWhere = function(obj, attrs) { + return _.where(obj, attrs, true); + }; + + // Return the maximum element or (element-based computation). + // Can't optimize arrays of integers longer than 65,535 elements. + // See: https://bugs.webkit.org/show_bug.cgi?id=80797 + _.max = function(obj, iterator, context) { + if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { + return Math.max.apply(Math, obj); + } + if (!iterator && _.isEmpty(obj)) return -Infinity; + var result = {computed : -Infinity, value: -Infinity}; + each(obj, function(value, index, list) { + var computed = iterator ? iterator.call(context, value, index, list) : value; + computed >= result.computed && (result = {value : value, computed : computed}); + }); + return result.value; + }; + + // Return the minimum element (or element-based computation). + _.min = function(obj, iterator, context) { + if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { + return Math.min.apply(Math, obj); + } + if (!iterator && _.isEmpty(obj)) return Infinity; + var result = {computed : Infinity, value: Infinity}; + each(obj, function(value, index, list) { + var computed = iterator ? iterator.call(context, value, index, list) : value; + computed < result.computed && (result = {value : value, computed : computed}); + }); + return result.value; + }; + + // Shuffle an array. + _.shuffle = function(obj) { + var rand; + var index = 0; + var shuffled = []; + each(obj, function(value) { + rand = _.random(index++); + shuffled[index - 1] = shuffled[rand]; + shuffled[rand] = value; + }); + return shuffled; + }; + + // An internal function to generate lookup iterators. + var lookupIterator = function(value) { + return _.isFunction(value) ? value : function(obj){ return obj[value]; }; + }; + + // Sort the object's values by a criterion produced by an iterator. + _.sortBy = function(obj, value, context) { + var iterator = lookupIterator(value); + return _.pluck(_.map(obj, function(value, index, list) { + return { + value : value, + index : index, + criteria : iterator.call(context, value, index, list) + }; + }).sort(function(left, right) { + var a = left.criteria; + var b = right.criteria; + if (a !== b) { + if (a > b || a === void 0) return 1; + if (a < b || b === void 0) return -1; + } + return left.index < right.index ? -1 : 1; + }), 'value'); + }; + + // An internal function used for aggregate "group by" operations. + var group = function(obj, value, context, behavior) { + var result = {}; + var iterator = lookupIterator(value || _.identity); + each(obj, function(value, index) { + var key = iterator.call(context, value, index, obj); + behavior(result, key, value); + }); + return result; + }; + + // Groups the object's values by a criterion. Pass either a string attribute + // to group by, or a function that returns the criterion. + _.groupBy = function(obj, value, context) { + return group(obj, value, context, function(result, key, value) { + (_.has(result, key) ? result[key] : (result[key] = [])).push(value); + }); + }; + + // Counts instances of an object that group by a certain criterion. Pass + // either a string attribute to count by, or a function that returns the + // criterion. + _.countBy = function(obj, value, context) { + return group(obj, value, context, function(result, key) { + if (!_.has(result, key)) result[key] = 0; + result[key]++; + }); + }; + + // Use a comparator function to figure out the smallest index at which + // an object should be inserted so as to maintain order. Uses binary search. + _.sortedIndex = function(array, obj, iterator, context) { + iterator = iterator == null ? _.identity : lookupIterator(iterator); + var value = iterator.call(context, obj); + var low = 0, high = array.length; + while (low < high) { + var mid = (low + high) >>> 1; + iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid; + } + return low; + }; + + // Safely convert anything iterable into a real, live array. + _.toArray = function(obj) { + if (!obj) return []; + if (_.isArray(obj)) return slice.call(obj); + if (obj.length === +obj.length) return _.map(obj, _.identity); + return _.values(obj); + }; + + // Return the number of elements in an object. + _.size = function(obj) { + if (obj == null) return 0; + return (obj.length === +obj.length) ? obj.length : _.keys(obj).length; + }; + + // Array Functions + // --------------- + + // Get the first element of an array. Passing **n** will return the first N + // values in the array. Aliased as `head` and `take`. The **guard** check + // allows it to work with `_.map`. + _.first = _.head = _.take = function(array, n, guard) { + if (array == null) return void 0; + return (n != null) && !guard ? slice.call(array, 0, n) : array[0]; + }; + + // Returns everything but the last entry of the array. Especially useful on + // the arguments object. Passing **n** will return all the values in + // the array, excluding the last N. The **guard** check allows it to work with + // `_.map`. + _.initial = function(array, n, guard) { + return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n)); + }; + + // Get the last element of an array. Passing **n** will return the last N + // values in the array. The **guard** check allows it to work with `_.map`. + _.last = function(array, n, guard) { + if (array == null) return void 0; + if ((n != null) && !guard) { + return slice.call(array, Math.max(array.length - n, 0)); + } else { + return array[array.length - 1]; + } + }; + + // Returns everything but the first entry of the array. Aliased as `tail` and `drop`. + // Especially useful on the arguments object. Passing an **n** will return + // the rest N values in the array. The **guard** + // check allows it to work with `_.map`. + _.rest = _.tail = _.drop = function(array, n, guard) { + return slice.call(array, (n == null) || guard ? 1 : n); + }; + + // Trim out all falsy values from an array. + _.compact = function(array) { + return _.filter(array, _.identity); + }; + + // Internal implementation of a recursive `flatten` function. + var flatten = function(input, shallow, output) { + each(input, function(value) { + if (_.isArray(value)) { + shallow ? push.apply(output, value) : flatten(value, shallow, output); + } else { + output.push(value); + } + }); + return output; + }; + + // Return a completely flattened version of an array. + _.flatten = function(array, shallow) { + return flatten(array, shallow, []); + }; + + // Return a version of the array that does not contain the specified value(s). + _.without = function(array) { + return _.difference(array, slice.call(arguments, 1)); + }; + + // Produce a duplicate-free version of the array. If the array has already + // been sorted, you have the option of using a faster algorithm. + // Aliased as `unique`. + _.uniq = _.unique = function(array, isSorted, iterator, context) { + if (_.isFunction(isSorted)) { + context = iterator; + iterator = isSorted; + isSorted = false; + } + var initial = iterator ? _.map(array, iterator, context) : array; + var results = []; + var seen = []; + each(initial, function(value, index) { + if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) { + seen.push(value); + results.push(array[index]); + } + }); + return results; + }; + + // Produce an array that contains the union: each distinct element from all of + // the passed-in arrays. + _.union = function() { + return _.uniq(concat.apply(ArrayProto, arguments)); + }; + + // Produce an array that contains every item shared between all the + // passed-in arrays. + _.intersection = function(array) { + var rest = slice.call(arguments, 1); + return _.filter(_.uniq(array), function(item) { + return _.every(rest, function(other) { + return _.indexOf(other, item) >= 0; + }); + }); + }; + + // Take the difference between one array and a number of other arrays. + // Only the elements present in just the first array will remain. + _.difference = function(array) { + var rest = concat.apply(ArrayProto, slice.call(arguments, 1)); + return _.filter(array, function(value){ return !_.contains(rest, value); }); + }; + + // Zip together multiple lists into a single array -- elements that share + // an index go together. + _.zip = function() { + var args = slice.call(arguments); + var length = _.max(_.pluck(args, 'length')); + var results = new Array(length); + for (var i = 0; i < length; i++) { + results[i] = _.pluck(args, "" + i); + } + return results; + }; + + // Converts lists into objects. Pass either a single array of `[key, value]` + // pairs, or two parallel arrays of the same length -- one of keys, and one of + // the corresponding values. + _.object = function(list, values) { + if (list == null) return {}; + var result = {}; + for (var i = 0, l = list.length; i < l; i++) { + if (values) { + result[list[i]] = values[i]; + } else { + result[list[i][0]] = list[i][1]; + } + } + return result; + }; + + // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**), + // we need this function. Return the position of the first occurrence of an + // item in an array, or -1 if the item is not included in the array. + // Delegates to **ECMAScript 5**'s native `indexOf` if available. + // If the array is large and already in sort order, pass `true` + // for **isSorted** to use binary search. + _.indexOf = function(array, item, isSorted) { + if (array == null) return -1; + var i = 0, l = array.length; + if (isSorted) { + if (typeof isSorted == 'number') { + i = (isSorted < 0 ? Math.max(0, l + isSorted) : isSorted); + } else { + i = _.sortedIndex(array, item); + return array[i] === item ? i : -1; + } + } + if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted); + for (; i < l; i++) if (array[i] === item) return i; + return -1; + }; + + // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available. + _.lastIndexOf = function(array, item, from) { + if (array == null) return -1; + var hasIndex = from != null; + if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) { + return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item); + } + var i = (hasIndex ? from : array.length); + while (i--) if (array[i] === item) return i; + return -1; + }; + + // Generate an integer Array containing an arithmetic progression. A port of + // the native Python `range()` function. See + // [the Python documentation](http://docs.python.org/library/functions.html#range). + _.range = function(start, stop, step) { + if (arguments.length <= 1) { + stop = start || 0; + start = 0; + } + step = arguments[2] || 1; + + var len = Math.max(Math.ceil((stop - start) / step), 0); + var idx = 0; + var range = new Array(len); + + while(idx < len) { + range[idx++] = start; + start += step; + } + + return range; + }; + + // Function (ahem) Functions + // ------------------ + + // Create a function bound to a given object (assigning `this`, and arguments, + // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if + // available. + _.bind = function(func, context) { + if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); + var args = slice.call(arguments, 2); + return function() { + return func.apply(context, args.concat(slice.call(arguments))); + }; + }; + + // Partially apply a function by creating a version that has had some of its + // arguments pre-filled, without changing its dynamic `this` context. + _.partial = function(func) { + var args = slice.call(arguments, 1); + return function() { + return func.apply(this, args.concat(slice.call(arguments))); + }; + }; + + // Bind all of an object's methods to that object. Useful for ensuring that + // all callbacks defined on an object belong to it. + _.bindAll = function(obj) { + var funcs = slice.call(arguments, 1); + if (funcs.length === 0) funcs = _.functions(obj); + each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); }); + return obj; + }; + + // Memoize an expensive function by storing its results. + _.memoize = function(func, hasher) { + var memo = {}; + hasher || (hasher = _.identity); + return function() { + var key = hasher.apply(this, arguments); + return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments)); + }; + }; + + // Delays a function for the given number of milliseconds, and then calls + // it with the arguments supplied. + _.delay = function(func, wait) { + var args = slice.call(arguments, 2); + return setTimeout(function(){ return func.apply(null, args); }, wait); + }; + + // Defers a function, scheduling it to run after the current call stack has + // cleared. + _.defer = function(func) { + return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1))); + }; + + // Returns a function, that, when invoked, will only be triggered at most once + // during a given window of time. + _.throttle = function(func, wait) { + var context, args, timeout, result; + var previous = 0; + var later = function() { + previous = new Date; + timeout = null; + result = func.apply(context, args); + }; + return function() { + var now = new Date; + var remaining = wait - (now - previous); + context = this; + args = arguments; + if (remaining <= 0) { + clearTimeout(timeout); + timeout = null; + previous = now; + result = func.apply(context, args); + } else if (!timeout) { + timeout = setTimeout(later, remaining); + } + return result; + }; + }; + + // Returns a function, that, as long as it continues to be invoked, will not + // be triggered. The function will be called after it stops being called for + // N milliseconds. If `immediate` is passed, trigger the function on the + // leading edge, instead of the trailing. + _.debounce = function(func, wait, immediate) { + var timeout, result; + return function() { + var context = this, args = arguments; + var later = function() { + timeout = null; + if (!immediate) result = func.apply(context, args); + }; + var callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) result = func.apply(context, args); + return result; + }; + }; + + // Returns a function that will be executed at most one time, no matter how + // often you call it. Useful for lazy initialization. + _.once = function(func) { + var ran = false, memo; + return function() { + if (ran) return memo; + ran = true; + memo = func.apply(this, arguments); + func = null; + return memo; + }; + }; + + // Returns the first function passed as an argument to the second, + // allowing you to adjust arguments, run code before and after, and + // conditionally execute the original function. + _.wrap = function(func, wrapper) { + return function() { + var args = [func]; + push.apply(args, arguments); + return wrapper.apply(this, args); + }; + }; + + // Returns a function that is the composition of a list of functions, each + // consuming the return value of the function that follows. + _.compose = function() { + var funcs = arguments; + return function() { + var args = arguments; + for (var i = funcs.length - 1; i >= 0; i--) { + args = [funcs[i].apply(this, args)]; + } + return args[0]; + }; + }; + + // Returns a function that will only be executed after being called N times. + _.after = function(times, func) { + if (times <= 0) return func(); + return function() { + if (--times < 1) { + return func.apply(this, arguments); + } + }; + }; + + // Object Functions + // ---------------- + + // Retrieve the names of an object's properties. + // Delegates to **ECMAScript 5**'s native `Object.keys` + _.keys = nativeKeys || function(obj) { + if (obj !== Object(obj)) throw new TypeError('Invalid object'); + var keys = []; + for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key; + return keys; + }; + + // Retrieve the values of an object's properties. + _.values = function(obj) { + var values = []; + for (var key in obj) if (_.has(obj, key)) values.push(obj[key]); + return values; + }; + + // Convert an object into a list of `[key, value]` pairs. + _.pairs = function(obj) { + var pairs = []; + for (var key in obj) if (_.has(obj, key)) pairs.push([key, obj[key]]); + return pairs; + }; + + // Invert the keys and values of an object. The values must be serializable. + _.invert = function(obj) { + var result = {}; + for (var key in obj) if (_.has(obj, key)) result[obj[key]] = key; + return result; + }; + + // Return a sorted list of the function names available on the object. + // Aliased as `methods` + _.functions = _.methods = function(obj) { + var names = []; + for (var key in obj) { + if (_.isFunction(obj[key])) names.push(key); + } + return names.sort(); + }; + + // Extend a given object with all the properties in passed-in object(s). + _.extend = function(obj) { + each(slice.call(arguments, 1), function(source) { + if (source) { + for (var prop in source) { + obj[prop] = source[prop]; + } + } + }); + return obj; + }; + + // Return a copy of the object only containing the whitelisted properties. + _.pick = function(obj) { + var copy = {}; + var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); + each(keys, function(key) { + if (key in obj) copy[key] = obj[key]; + }); + return copy; + }; + + // Return a copy of the object without the blacklisted properties. + _.omit = function(obj) { + var copy = {}; + var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); + for (var key in obj) { + if (!_.contains(keys, key)) copy[key] = obj[key]; + } + return copy; + }; + + // Fill in a given object with default properties. + _.defaults = function(obj) { + each(slice.call(arguments, 1), function(source) { + if (source) { + for (var prop in source) { + if (obj[prop] == null) obj[prop] = source[prop]; + } + } + }); + return obj; + }; + + // Create a (shallow-cloned) duplicate of an object. + _.clone = function(obj) { + if (!_.isObject(obj)) return obj; + return _.isArray(obj) ? obj.slice() : _.extend({}, obj); + }; + + // Invokes interceptor with the obj, and then returns obj. + // The primary purpose of this method is to "tap into" a method chain, in + // order to perform operations on intermediate results within the chain. + _.tap = function(obj, interceptor) { + interceptor(obj); + return obj; + }; + + // Internal recursive comparison function for `isEqual`. + var eq = function(a, b, aStack, bStack) { + // Identical objects are equal. `0 === -0`, but they aren't identical. + // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal. + if (a === b) return a !== 0 || 1 / a == 1 / b; + // A strict comparison is necessary because `null == undefined`. + if (a == null || b == null) return a === b; + // Unwrap any wrapped objects. + if (a instanceof _) a = a._wrapped; + if (b instanceof _) b = b._wrapped; + // Compare `[[Class]]` names. + var className = toString.call(a); + if (className != toString.call(b)) return false; + switch (className) { + // Strings, numbers, dates, and booleans are compared by value. + case '[object String]': + // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is + // equivalent to `new String("5")`. + return a == String(b); + case '[object Number]': + // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for + // other numeric values. + return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b); + case '[object Date]': + case '[object Boolean]': + // Coerce dates and booleans to numeric primitive values. Dates are compared by their + // millisecond representations. Note that invalid dates with millisecond representations + // of `NaN` are not equivalent. + return +a == +b; + // RegExps are compared by their source patterns and flags. + case '[object RegExp]': + return a.source == b.source && + a.global == b.global && + a.multiline == b.multiline && + a.ignoreCase == b.ignoreCase; + } + if (typeof a != 'object' || typeof b != 'object') return false; + // Assume equality for cyclic structures. The algorithm for detecting cyclic + // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. + var length = aStack.length; + while (length--) { + // Linear search. Performance is inversely proportional to the number of + // unique nested structures. + if (aStack[length] == a) return bStack[length] == b; + } + // Add the first object to the stack of traversed objects. + aStack.push(a); + bStack.push(b); + var size = 0, result = true; + // Recursively compare objects and arrays. + if (className == '[object Array]') { + // Compare array lengths to determine if a deep comparison is necessary. + size = a.length; + result = size == b.length; + if (result) { + // Deep compare the contents, ignoring non-numeric properties. + while (size--) { + if (!(result = eq(a[size], b[size], aStack, bStack))) break; + } + } + } else { + // Objects with different constructors are not equivalent, but `Object`s + // from different frames are. + var aCtor = a.constructor, bCtor = b.constructor; + if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) && + _.isFunction(bCtor) && (bCtor instanceof bCtor))) { + return false; + } + // Deep compare objects. + for (var key in a) { + if (_.has(a, key)) { + // Count the expected number of properties. + size++; + // Deep compare each member. + if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break; + } + } + // Ensure that both objects contain the same number of properties. + if (result) { + for (key in b) { + if (_.has(b, key) && !(size--)) break; + } + result = !size; + } + } + // Remove the first object from the stack of traversed objects. + aStack.pop(); + bStack.pop(); + return result; + }; + + // Perform a deep comparison to check if two objects are equal. + _.isEqual = function(a, b) { + return eq(a, b, [], []); + }; + + // Is a given array, string, or object empty? + // An "empty" object has no enumerable own-properties. + _.isEmpty = function(obj) { + if (obj == null) return true; + if (_.isArray(obj) || _.isString(obj)) return obj.length === 0; + for (var key in obj) if (_.has(obj, key)) return false; + return true; + }; + + // Is a given value a DOM element? + _.isElement = function(obj) { + return !!(obj && obj.nodeType === 1); + }; + + // Is a given value an array? + // Delegates to ECMA5's native Array.isArray + _.isArray = nativeIsArray || function(obj) { + return toString.call(obj) == '[object Array]'; + }; + + // Is a given variable an object? + _.isObject = function(obj) { + return obj === Object(obj); + }; + + // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp. + each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) { + _['is' + name] = function(obj) { + return toString.call(obj) == '[object ' + name + ']'; + }; + }); + + // Define a fallback version of the method in browsers (ahem, IE), where + // there isn't any inspectable "Arguments" type. + if (!_.isArguments(arguments)) { + _.isArguments = function(obj) { + return !!(obj && _.has(obj, 'callee')); + }; + } + + // Optimize `isFunction` if appropriate. + if (typeof (/./) !== 'function') { + _.isFunction = function(obj) { + return typeof obj === 'function'; + }; + } + + // Is a given object a finite number? + _.isFinite = function(obj) { + return isFinite(obj) && !isNaN(parseFloat(obj)); + }; + + // Is the given value `NaN`? (NaN is the only number which does not equal itself). + _.isNaN = function(obj) { + return _.isNumber(obj) && obj != +obj; + }; + + // Is a given value a boolean? + _.isBoolean = function(obj) { + return obj === true || obj === false || toString.call(obj) == '[object Boolean]'; + }; + + // Is a given value equal to null? + _.isNull = function(obj) { + return obj === null; + }; + + // Is a given variable undefined? + _.isUndefined = function(obj) { + return obj === void 0; + }; + + // Shortcut function for checking if an object has a given property directly + // on itself (in other words, not on a prototype). + _.has = function(obj, key) { + return hasOwnProperty.call(obj, key); + }; + + // Utility Functions + // ----------------- + + // Run Underscore.js in *noConflict* mode, returning the `_` variable to its + // previous owner. Returns a reference to the Underscore object. + _.noConflict = function() { + root._ = previousUnderscore; + return this; + }; + + // Keep the identity function around for default iterators. + _.identity = function(value) { + return value; + }; + + // Run a function **n** times. + _.times = function(n, iterator, context) { + var accum = Array(n); + for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i); + return accum; + }; + + // Return a random integer between min and max (inclusive). + _.random = function(min, max) { + if (max == null) { + max = min; + min = 0; + } + return min + Math.floor(Math.random() * (max - min + 1)); + }; + + // List of HTML entities for escaping. + var entityMap = { + escape: { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''', + '/': '/' + } + }; + entityMap.unescape = _.invert(entityMap.escape); + + // Regexes containing the keys and values listed immediately above. + var entityRegexes = { + escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'), + unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g') + }; + + // Functions for escaping and unescaping strings to/from HTML interpolation. + _.each(['escape', 'unescape'], function(method) { + _[method] = function(string) { + if (string == null) return ''; + return ('' + string).replace(entityRegexes[method], function(match) { + return entityMap[method][match]; + }); + }; + }); + + // If the value of the named property is a function then invoke it; + // otherwise, return it. + _.result = function(object, property) { + if (object == null) return null; + var value = object[property]; + return _.isFunction(value) ? value.call(object) : value; + }; + + // Add your own custom functions to the Underscore object. + _.mixin = function(obj) { + each(_.functions(obj), function(name){ + var func = _[name] = obj[name]; + _.prototype[name] = function() { + var args = [this._wrapped]; + push.apply(args, arguments); + return result.call(this, func.apply(_, args)); + }; + }); + }; + + // Generate a unique integer id (unique within the entire client session). + // Useful for temporary DOM ids. + var idCounter = 0; + _.uniqueId = function(prefix) { + var id = ++idCounter + ''; + return prefix ? prefix + id : id; + }; + + // By default, Underscore uses ERB-style template delimiters, change the + // following template settings to use alternative delimiters. + _.templateSettings = { + evaluate : /<%([\s\S]+?)%>/g, + interpolate : /<%=([\s\S]+?)%>/g, + escape : /<%-([\s\S]+?)%>/g + }; + + // When customizing `templateSettings`, if you don't want to define an + // interpolation, evaluation or escaping regex, we need one that is + // guaranteed not to match. + var noMatch = /(.)^/; + + // Certain characters need to be escaped so that they can be put into a + // string literal. + var escapes = { + "'": "'", + '\\': '\\', + '\r': 'r', + '\n': 'n', + '\t': 't', + '\u2028': 'u2028', + '\u2029': 'u2029' + }; + + var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; + + // JavaScript micro-templating, similar to John Resig's implementation. + // Underscore templating handles arbitrary delimiters, preserves whitespace, + // and correctly escapes quotes within interpolated code. + _.template = function(text, data, settings) { + var render; + settings = _.defaults({}, settings, _.templateSettings); + + // Combine delimiters into one regular expression via alternation. + var matcher = new RegExp([ + (settings.escape || noMatch).source, + (settings.interpolate || noMatch).source, + (settings.evaluate || noMatch).source + ].join('|') + '|$', 'g'); + + // Compile the template source, escaping string literals appropriately. + var index = 0; + var source = "__p+='"; + text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { + source += text.slice(index, offset) + .replace(escaper, function(match) { return '\\' + escapes[match]; }); + + if (escape) { + source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; + } + if (interpolate) { + source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; + } + if (evaluate) { + source += "';\n" + evaluate + "\n__p+='"; + } + index = offset + match.length; + return match; + }); + source += "';\n"; + + // If a variable is not specified, place data values in local scope. + if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; + + source = "var __t,__p='',__j=Array.prototype.join," + + "print=function(){__p+=__j.call(arguments,'');};\n" + + source + "return __p;\n"; + + try { + render = new Function(settings.variable || 'obj', '_', source); + } catch (e) { + e.source = source; + throw e; + } + + if (data) return render(data, _); + var template = function(data) { + return render.call(this, data, _); + }; + + // Provide the compiled function source as a convenience for precompilation. + template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; + + return template; + }; + + // Add a "chain" function, which will delegate to the wrapper. + _.chain = function(obj) { + return _(obj).chain(); + }; + + // OOP + // --------------- + // If Underscore is called as a function, it returns a wrapped object that + // can be used OO-style. This wrapper holds altered versions of all the + // underscore functions. Wrapped objects may be chained. + + // Helper function to continue chaining intermediate results. + var result = function(obj) { + return this._chain ? _(obj).chain() : obj; + }; + + // Add all of the Underscore functions to the wrapper object. + _.mixin(_); + + // Add all mutator Array functions to the wrapper. + each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { + var method = ArrayProto[name]; + _.prototype[name] = function() { + var obj = this._wrapped; + method.apply(obj, arguments); + if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0]; + return result.call(this, obj); + }; + }); + + // Add all accessor Array functions to the wrapper. + each(['concat', 'join', 'slice'], function(name) { + var method = ArrayProto[name]; + _.prototype[name] = function() { + return result.call(this, method.apply(this._wrapped, arguments)); + }; + }); + + _.extend(_.prototype, { + + // Start chaining a wrapped Underscore object. + chain: function() { + this._chain = true; + return this; + }, + + // Extracts the result from a wrapped and chained object. + value: function() { + return this._wrapped; + } + + }); + +}).call(this); + +/*global _: false, $: false, localStorage: false, process: true, + XMLHttpRequest: false, XDomainRequest: false, exports: false, + require: false, setTimeout: true */ +(function(root) { + root.Parse = root.Parse || {}; + /** + * Contains all Parse API classes and functions. + * @name Parse + * @namespace + * + * Contains all Parse API classes and functions. + */ + var Parse = root.Parse; + + var req = typeof(require) === 'function' ? require : null; + // Load references to other dependencies + if (typeof(XMLHttpRequest) !== 'undefined') { + Parse.XMLHttpRequest = XMLHttpRequest; + } else if (typeof(require) === 'function' && + typeof(require.ensure) === 'undefined') { + Parse.XMLHttpRequest = req('xmlhttprequest').XMLHttpRequest; + } + // Import Parse's local copy of underscore. + if (typeof(exports) !== 'undefined' && exports._) { + // We're running in a CommonJS environment + Parse._ = exports._.noConflict(); + exports.Parse = Parse; + } else { + Parse._ = _.noConflict(); + } + + // If jQuery or Zepto has been included, grab a reference to it. + if (typeof($) !== "undefined") { + Parse.$ = $; + } + + // Helpers + // ------- + + // Shared empty constructor function to aid in prototype-chain creation. + var EmptyConstructor = function() {}; + + + // Helper function to correctly set up the prototype chain, for subclasses. + // Similar to `goog.inherits`, but uses a hash of prototype properties and + // class properties to be extended. + var inherits = function(parent, protoProps, staticProps) { + var child; + + // The constructor function for the new subclass is either defined by you + // (the "constructor" property in your `extend` definition), or defaulted + // by us to simply call the parent's constructor. + if (protoProps && protoProps.hasOwnProperty('constructor')) { + child = protoProps.constructor; + } else { + /** @ignore */ + child = function(){ parent.apply(this, arguments); }; + } + + // Inherit class (static) properties from parent. + Parse._.extend(child, parent); + + // Set the prototype chain to inherit from `parent`, without calling + // `parent`'s constructor function. + EmptyConstructor.prototype = parent.prototype; + child.prototype = new EmptyConstructor(); + + // Add prototype properties (instance properties) to the subclass, + // if supplied. + if (protoProps) { + Parse._.extend(child.prototype, protoProps); + } + + // Add static properties to the constructor function, if supplied. + if (staticProps) { + Parse._.extend(child, staticProps); + } + + // Correctly set child's `prototype.constructor`. + child.prototype.constructor = child; + + // Set a convenience property in case the parent's prototype is + // needed later. + child.__super__ = parent.prototype; + + return child; + }; + + // Set the server for Parse to talk to. + Parse.serverURL = "https://api.parse.com"; + + // Check whether we are running in Node.js. + if (typeof(process) !== "undefined" && + process.versions && + process.versions.node) { + Parse._isNode = true; + } + + /** + * Call this method first to set up your authentication tokens for Parse. + * You can get your keys from the Data Browser on parse.com. + * @param {String} applicationId Your Parse Application ID. + * @param {String} javaScriptKey Your Parse JavaScript Key. + * @param {String} masterKey (optional) Your Parse Master Key. (Node.js only!) + */ + Parse.initialize = function(applicationId, javaScriptKey, masterKey) { + if (masterKey) { + throw "Parse.initialize() was passed a Master Key, which is only " + + "allowed from within Node.js."; + } + Parse._initialize(applicationId, javaScriptKey); + }; + + /** + * Call this method first to set up master authentication tokens for Parse. + * This method is for Parse's own private use. + * @param {String} applicationId Your Parse Application ID. + * @param {String} javaScriptKey Your Parse JavaScript Key. + * @param {String} masterKey Your Parse Master Key. + */ + Parse._initialize = function(applicationId, javaScriptKey, masterKey) { + Parse.applicationId = applicationId; + Parse.javaScriptKey = javaScriptKey; + Parse.masterKey = masterKey; + Parse._useMasterKey = false; + }; + + // If we're running in node.js, allow using the master key. + if (Parse._isNode) { + Parse.initialize = Parse._initialize; + + Parse.Cloud = Parse.Cloud || {}; + /** + * Switches the Parse SDK to using the Master key. The Master key grants + * priveleged access to the data in Parse and can be used to bypass ACLs and + * other restrictions that are applied to the client SDKs. + *

Available in Cloud Code and Node.js only. + *

+ */ + Parse.Cloud.useMasterKey = function() { + Parse._useMasterKey = true; + }; + } + + /** + * Returns prefix for Storage keys used by this instance of Parse. + * @param {String} path The relative suffix to append to it. + * null or undefined is treated as the empty string. + * @return {String} The full key name. + */ + Parse._getParsePath = function(path) { + if (!Parse.applicationId) { + throw "You need to call Parse.initialize before using Parse."; + } + if (!path) { + path = ""; + } + if (!Parse._.isString(path)) { + throw "Tried to get a Storage path that wasn't a String."; + } + if (path[0] === "/") { + path = path.substring(1); + } + return "Parse/" + Parse.applicationId + "/" + path; + }; + + /** + * Returns a Promise that is resolved with the unique string for this app on + * this machine. + * Gets reset when Storage is cleared. + */ + Parse._installationId = null; + Parse._getInstallationId = function() { + // See if it's cached in RAM. + if (Parse._installationId) { + return Parse.Promise.as(Parse._installationId); + } + + // Try to get it from Storage. + var path = Parse._getParsePath("installationId"); + return (Parse.Storage.getItemAsync(path) + .then(function(value) { + Parse._installationId = value; + + if (!Parse._installationId || Parse._installationId === "") { + // It wasn't in Storage, so create a new one. + var hexOctet = function() { + return ( + Math.floor((1+Math.random())*0x10000).toString(16).substring(1) + ); + }; + Parse._installationId = ( + hexOctet() + hexOctet() + "-" + + hexOctet() + "-" + + hexOctet() + "-" + + hexOctet() + "-" + + hexOctet() + hexOctet() + hexOctet()); + return Parse.Storage.setItemAsync(path, Parse._installationId); + } + + return Parse.Promise.as(Parse._installationId); + }) + ); + }; + + Parse._parseDate = function(iso8601) { + var regexp = new RegExp( + "^([0-9]{1,4})-([0-9]{1,2})-([0-9]{1,2})" + "T" + + "([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2})" + + "(.([0-9]+))?" + "Z$"); + var match = regexp.exec(iso8601); + if (!match) { + return null; + } + + var year = match[1] || 0; + var month = (match[2] || 1) - 1; + var day = match[3] || 0; + var hour = match[4] || 0; + var minute = match[5] || 0; + var second = match[6] || 0; + var milli = match[8] || 0; + + return new Date(Date.UTC(year, month, day, hour, minute, second, milli)); + }; + + Parse._ajaxIE8 = function(method, url, data) { + var promise = new Parse.Promise(); + var xdr = new XDomainRequest(); + xdr.onload = function() { + var response; + try { + response = JSON.parse(xdr.responseText); + } catch (e) { + promise.reject(e); + } + if (response) { + promise.resolve(response); + } + }; + xdr.onerror = xdr.ontimeout = function() { + // Let's fake a real error message. + var fakeResponse = { + responseText: JSON.stringify({ + code: Parse.Error.X_DOMAIN_REQUEST, + error: "IE's XDomainRequest does not supply error info." + }) + }; + promise.reject(fakeResponse); + }; + xdr.onprogress = function() {}; + xdr.open(method, url); + xdr.send(data); + return promise; + }; + + Parse._useXDomainRequest = function() { + if (typeof(XDomainRequest) !== "undefined") { + // We're in IE 8+. + if ('withCredentials' in new XMLHttpRequest()) { + // We're in IE 10+. + return false; + } + return true; + } + return false; + }; + + + Parse._ajax = function(method, url, data, success, error) { + var options = { + success: success, + error: error + }; + + if (Parse._useXDomainRequest()) { + return Parse._ajaxIE8(method, url, data)._thenRunCallbacks(options); + } + + var promise = new Parse.Promise(); + var attempts = 0; + + var dispatch = function() { + var handled = false; + var xhr = new Parse.XMLHttpRequest(); + + xhr.onreadystatechange = function() { + if (xhr.readyState === 4) { + if (handled) { + return; + } + handled = true; + + if (xhr.status >= 200 && xhr.status < 300) { + var response; + try { + response = JSON.parse(xhr.responseText); + } catch (e) { + promise.reject(e); + } + if (response) { + promise.resolve(response, xhr.status, xhr); + } + } else if (xhr.status >= 500) { // Retry on 5XX + if (++attempts < 5) { + // Exponentially-growing delay + var delay = Math.round( + Math.random() * 125 * Math.pow(2, attempts) + ); + setTimeout(dispatch, delay); + } else { + // After 5 retries, fail + promise.reject(xhr); + } + } else { + promise.reject(xhr); + } + } + }; + + xhr.open(method, url, true); + xhr.setRequestHeader('Content-Type', 'text/plain'); // avoid pre-flight. + if (Parse._isNode) { + // Add a special user agent just for request from node.js. + xhr.setRequestHeader("User-Agent", + "Parse/" + Parse.VERSION + + " (NodeJS " + process.versions.node + ")"); + } + xhr.send(data); + }; + + dispatch(); + return promise._thenRunCallbacks(options); + }; + + // A self-propagating extend function. + Parse._extend = function(protoProps, classProps) { + var child = inherits(this, protoProps, classProps); + child.extend = this.extend; + return child; + }; + + /** + * Options: + * route: is classes, users, login, etc. + * objectId: null if there is no associated objectId. + * method: the http method for the REST API. + * dataObject: the payload as an object, or null if there is none. + * useMasterKey: overrides whether to use the master key if set. + * @ignore + */ + Parse._request = function(options) { + var route = options.route; + var className = options.className; + var objectId = options.objectId; + var method = options.method; + var useMasterKey = options.useMasterKey; + var sessionToken = options.sessionToken; + var dataObject = options.data; + + if (!Parse.applicationId) { + throw "You must specify your applicationId using Parse.initialize."; + } + + if (!Parse.javaScriptKey && !Parse.masterKey) { + throw "You must specify a key using Parse.initialize."; + } + + + if (route !== "batch" && + route !== "classes" && + route !== "events" && + route !== "files" && + route !== "functions" && + route !== "login" && + route !== "logout" && + route !== "push" && + route !== "requestPasswordReset" && + route !== "rest_verify_analytics" && + route !== "users" && + route !== "jobs" && + route !== "config" && + route !== "sessions" && + route !== "upgradeToRevocableSession") { + throw "Bad route: '" + route + "'."; + } + + var url = Parse.serverURL; + if (url.charAt(url.length - 1) !== "/") { + url += "/"; + } + url += "1/" + route; + if (className) { + url += "/" + className; + } + if (objectId) { + url += "/" + objectId; + } + + dataObject = Parse._.clone(dataObject || {}); + if (method !== "POST") { + dataObject._method = method; + method = "POST"; + } + + if (Parse._.isUndefined(useMasterKey)) { + useMasterKey = Parse._useMasterKey; + } + + dataObject._ApplicationId = Parse.applicationId; + if (!useMasterKey) { + dataObject._JavaScriptKey = Parse.javaScriptKey; + } else if (!Parse.masterKey) { + throw new Error('Cannot use the Master Key, it has not been provided.'); + } else { + dataObject._MasterKey = Parse.masterKey; + } + + dataObject._ClientVersion = Parse.VERSION; + + return Parse._getInstallationId().then(function(iid) { + dataObject._InstallationId = iid; + + if (sessionToken) { + return Parse.Promise.as({ _sessionToken: sessionToken }); + } + if (!Parse.User._canUseCurrentUser()) { + return Parse.Promise.as(null); + } + + return Parse.User._currentAsync(); + }).then(function(currentUser) { + if (currentUser && currentUser._sessionToken) { + dataObject._SessionToken = currentUser._sessionToken; + } + + if (Parse.User._isRevocableSessionEnabled) { + dataObject._RevocableSession = '1'; + } + + var data = JSON.stringify(dataObject); + + return Parse._ajax(method, url, data); + }).then(null, function(response) { + // Transform the error into an instance of Parse.Error by trying to parse + // the error string as JSON. + var error; + if (response && response.responseText) { + try { + var errorJSON = JSON.parse(response.responseText); + error = new Parse.Error(errorJSON.code, errorJSON.error); + } catch (e) { + // If we fail to parse the error text, that's okay. + error = new Parse.Error( + Parse.Error.INVALID_JSON, + "Received an error with invalid JSON from Parse: " + + response.responseText); + } + } else { + error = new Parse.Error( + Parse.Error.CONNECTION_FAILED, + "XMLHttpRequest failed: " + JSON.stringify(response)); + } + // By explicitly returning a rejected Promise, this will work with + // either jQuery or Promises/A semantics. + return Parse.Promise.error(error); + }); + }; + + // Helper function to get a value from a Backbone object as a property + // or as a function. + Parse._getValue = function(object, prop) { + if (!(object && object[prop])) { + return null; + } + return Parse._.isFunction(object[prop]) ? object[prop]() : object[prop]; + }; + + /** + * Converts a value in a Parse Object into the appropriate representation. + * This is the JS equivalent of Java's Parse.maybeReferenceAndEncode(Object) + * if seenObjects is falsey. Otherwise any Parse.Objects not in + * seenObjects will be fully embedded rather than encoded + * as a pointer. This array will be used to prevent going into an infinite + * loop because we have circular references. If seenObjects + * is set, then none of the Parse Objects that are serialized can be dirty. + */ + Parse._encode = function(value, seenObjects, disallowObjects) { + var _ = Parse._; + if (value instanceof Parse.Object) { + if (disallowObjects) { + throw "Parse.Objects not allowed here"; + } + if (!seenObjects || _.include(seenObjects, value) || !value._hasData) { + return value._toPointer(); + } + if (!value.dirty()) { + seenObjects = seenObjects.concat(value); + return Parse._encode(value._toFullJSON(seenObjects), + seenObjects, + disallowObjects); + } + throw "Tried to save an object with a pointer to a new, unsaved object."; + } + if (value instanceof Parse.ACL) { + return value.toJSON(); + } + if (_.isDate(value)) { + if (isNaN(value)) { + throw new Error('Cannot encode invalid Date'); + } + return { "__type": "Date", "iso": value.toJSON() }; + } + if (value instanceof Parse.GeoPoint) { + return value.toJSON(); + } + if (_.isArray(value)) { + return _.map(value, function(x) { + return Parse._encode(x, seenObjects, disallowObjects); + }); + } + if (_.isRegExp(value)) { + return value.source; + } + if (value instanceof Parse.Relation) { + return value.toJSON(); + } + if (value instanceof Parse.Op) { + return value.toJSON(); + } + if (value instanceof Parse.File) { + if (!value.url()) { + throw "Tried to save an object containing an unsaved file."; + } + return { + __type: "File", + name: value.name(), + url: value.url() + }; + } + if (_.isObject(value)) { + var output = {}; + Parse._objectEach(value, function(v, k) { + output[k] = Parse._encode(v, seenObjects, disallowObjects); + }); + return output; + } + return value; + }; + + /** + * The inverse function of Parse._encode. + * TODO: make decode not mutate value. + */ + Parse._decode = function(key, value) { + var _ = Parse._; + if (!_.isObject(value)) { + return value; + } + if (_.isArray(value)) { + Parse._arrayEach(value, function(v, k) { + value[k] = Parse._decode(k, v); + }); + return value; + } + if (value instanceof Parse.Object) { + return value; + } + if (value instanceof Parse.File) { + return value; + } + if (value instanceof Parse.Op) { + return value; + } + if (value.__op) { + return Parse.Op._decode(value); + } + if (value.__type === "Pointer" && value.className) { + var pointer = Parse.Object._create(value.className); + pointer._finishFetch({ objectId: value.objectId }, false); + return pointer; + } + if (value.__type === "Object" && value.className) { + // It's an Object included in a query result. + var className = value.className; + delete value.__type; + delete value.className; + var object = Parse.Object._create(className); + object._finishFetch(value, true); + return object; + } + if (value.__type === "Date") { + return Parse._parseDate(value.iso); + } + if (value.__type === "GeoPoint") { + return new Parse.GeoPoint({ + latitude: value.latitude, + longitude: value.longitude + }); + } + if (key === "ACL") { + if (value instanceof Parse.ACL) { + return value; + } + return new Parse.ACL(value); + } + if (value.__type === "Relation") { + var relation = new Parse.Relation(null, key); + relation.targetClassName = value.className; + return relation; + } + if (value.__type === "File") { + var file = new Parse.File(value.name); + file._url = value.url; + return file; + } + Parse._objectEach(value, function(v, k) { + value[k] = Parse._decode(k, v); + }); + return value; + }; + + Parse._arrayEach = Parse._.each; + + /** + * Does a deep traversal of every item in object, calling func on every one. + * @param {Object} object The object or array to traverse deeply. + * @param {Function} func The function to call for every item. It will + * be passed the item as an argument. If it returns a truthy value, that + * value will replace the item in its parent container. + * @returns {} the result of calling func on the top-level object itself. + */ + Parse._traverse = function(object, func, seen) { + if (object instanceof Parse.Object) { + seen = seen || []; + if (Parse._.indexOf(seen, object) >= 0) { + // We've already visited this object in this call. + return; + } + seen.push(object); + Parse._traverse(object.attributes, func, seen); + return func(object); + } + if (object instanceof Parse.Relation || object instanceof Parse.File) { + // Nothing needs to be done, but we don't want to recurse into the + // object's parent infinitely, so we catch this case. + return func(object); + } + if (Parse._.isArray(object)) { + Parse._.each(object, function(child, index) { + var newChild = Parse._traverse(child, func, seen); + if (newChild) { + object[index] = newChild; + } + }); + return func(object); + } + if (Parse._.isObject(object)) { + Parse._each(object, function(child, key) { + var newChild = Parse._traverse(child, func, seen); + if (newChild) { + object[key] = newChild; + } + }); + return func(object); + } + return func(object); + }; + + /** + * This is like _.each, except: + * * it doesn't work for so-called array-like objects, + * * it does work for dictionaries with a "length" attribute. + */ + Parse._objectEach = Parse._each = function(obj, callback) { + var _ = Parse._; + if (_.isObject(obj)) { + _.each(_.keys(obj), function(key) { + callback(obj[key], key); + }); + } else { + _.each(obj, callback); + } + }; + + // Helper function to check null or undefined. + Parse._isNullOrUndefined = function(x) { + return Parse._.isNull(x) || Parse._.isUndefined(x); + }; +}(this)); + +/* global require: false, localStorage: false */ +(function(root) { + root.Parse = root.Parse || {}; + var Parse = root.Parse; + + var Storage = { + async: false, + }; + + var hasLocalStorage = (typeof localStorage !== 'undefined'); + if (hasLocalStorage) { + try { + localStorage.setItem('supported', true); + localStorage.removeItem('supported'); + } catch(e) { + hasLocalStorage = false; + } + } + if (hasLocalStorage) { + Storage.getItem = function(path) { + return localStorage.getItem(path); + }; + + Storage.setItem = function(path, value) { + return localStorage.setItem(path, value); + }; + + Storage.removeItem = function(path) { + return localStorage.removeItem(path); + }; + + Storage.clear = function() { + return localStorage.clear(); + }; + } else if (typeof require === 'function') { + var AsyncStorage; + try { + AsyncStorage = eval("require('AsyncStorage')"); // jshint ignore:line + + Storage.async = true; + + Storage.getItemAsync = function(path) { + var p = new Parse.Promise(); + AsyncStorage.getItem(path, function(err, value) { + if (err) { + p.reject(err); + } else { + p.resolve(value); + } + }); + return p; + }; + + Storage.setItemAsync = function(path, value) { + var p = new Parse.Promise(); + AsyncStorage.setItem(path, value, function(err) { + if (err) { + p.reject(err); + } else { + p.resolve(value); + } + }); + return p; + }; + + Storage.removeItemAsync = function(path) { + var p = new Parse.Promise(); + AsyncStorage.removeItem(path, function(err) { + if (err) { + p.reject(err); + } else { + p.resolve(); + } + }); + return p; + }; + + Storage.clear = function() { + AsyncStorage.clear(); + }; + } catch (e) { } + } + if (!Storage.async && !Storage.getItem) { + var memMap = Storage.inMemoryMap = {}; + Storage.getItem = function(path) { + if (memMap.hasOwnProperty(path)) { + return memMap[path]; + } + return null; + }; + + Storage.setItem = function(path, value) { + memMap[path] = String(value); + }; + + Storage.removeItem = function(path) { + delete memMap[path]; + }; + + Storage.clear = function() { + for (var key in memMap) { + if (memMap.hasOwnProperty(key)) { + delete memMap[key]; + } + } + }; + } + + // We can use synchronous methods from async scenarios, but not vice-versa + if (!Storage.async) { + Storage.getItemAsync = function(path) { + return Parse.Promise.as( + Storage.getItem(path) + ); + }; + + Storage.setItemAsync = function(path, value) { + Storage.setItem(path, value); + return Parse.Promise.as(value); + }; + + Storage.removeItemAsync = function(path) { + return Parse.Promise.as( + Storage.removeItem(path) + ); + }; + } + + Parse.Storage = Storage; + +})(this); + +(function(root) { + root.Parse = root.Parse || {}; + var Parse = root.Parse; + var _ = Parse._; + + /** + * @namespace Provides an interface to Parse's logging and analytics backend. + */ + Parse.Analytics = Parse.Analytics || {}; + + _.extend(Parse.Analytics, /** @lends Parse.Analytics */ { + /** + * Tracks the occurrence of a custom event with additional dimensions. + * Parse will store a data point at the time of invocation with the given + * event name. + * + * Dimensions will allow segmentation of the occurrences of this custom + * event. Keys and values should be {@code String}s, and will throw + * otherwise. + * + * To track a user signup along with additional metadata, consider the + * following: + *
+     * var dimensions = {
+     *  gender: 'm',
+     *  source: 'web',
+     *  dayType: 'weekend'
+     * };
+     * Parse.Analytics.track('signup', dimensions);
+     * 
+ * + * There is a default limit of 8 dimensions per event tracked. + * + * @param {String} name The name of the custom event to report to Parse as + * having happened. + * @param {Object} dimensions The dictionary of information by which to + * segment this event. + * @param {Object} options A Backbone-style callback object. + * @return {Parse.Promise} A promise that is resolved when the round-trip + * to the server completes. + */ + track: function(name, dimensions, options) { + name = name || ''; + name = name.replace(/^\s*/, ''); + name = name.replace(/\s*$/, ''); + if (name.length === 0) { + throw 'A name for the custom event must be provided'; + } + + _.each(dimensions, function(val, key) { + if (!_.isString(key) || !_.isString(val)) { + throw 'track() dimensions expects keys and values of type "string".'; + } + }); + + options = options || {}; + return Parse._request({ + route: 'events', + className: name, + method: 'POST', + data: { dimensions: dimensions } + })._thenRunCallbacks(options); + } + }); +}(this)); + +(function(root) { + root.Parse = root.Parse || {}; + var Parse = root.Parse; + var _ = Parse._; + + /** + * @class Parse.Config is a local representation of configuration data that + * can be set from the Parse dashboard. + */ + Parse.Config = function() { + this.attributes = {}; + this._escapedAttributes = {}; + }; + + /** + * Retrieves the most recently-fetched configuration object, either from + * memory or from local storage if necessary. + * + * @return {Parse.Config} The most recently-fetched Parse.Config if it + * exists, else an empty Parse.Config. + */ + Parse.Config.current = function() { + if (Parse.Config._currentConfig) { + return Parse.Config._currentConfig; + } + + var config = new Parse.Config(); + + if (Parse.Storage.async) { + return config; + } + + var configData = Parse.Storage.getItem(Parse._getParsePath( + Parse.Config._CURRENT_CONFIG_KEY)); + + if (configData) { + config._finishFetch(JSON.parse(configData)); + Parse.Config._currentConfig = config; + } + return config; + }; + + /** + * Gets a new configuration object from the server. + * @param {Object} options A Backbone-style options object. + * Valid options are:
    + *
  • success: Function to call when the get completes successfully. + *
  • error: Function to call when the get fails. + *
+ * @return {Parse.Promise} A promise that is resolved with a newly-created + * configuration object when the get completes. + */ + Parse.Config.get = function(options) { + options = options || {}; + + var request = Parse._request({ + route: "config", + method: "GET", + }); + + return request.then(function(response) { + if (!response || !response.params) { + var errorObject = new Parse.Error( + Parse.Error.INVALID_JSON, + "Config JSON response invalid."); + return Parse.Promise.error(errorObject); + } + + var config = new Parse.Config(); + config._finishFetch(response); + Parse.Config._currentConfig = config; + return config; + })._thenRunCallbacks(options); + }; + + Parse.Config.prototype = { + + /** + * Gets the HTML-escaped value of an attribute. + */ + escape: function(attr) { + var html = this._escapedAttributes[attr]; + if (html) { + return html; + } + var val = this.attributes[attr]; + var escaped; + if (Parse._isNullOrUndefined(val)) { + escaped = ''; + } else { + escaped = _.escape(val.toString()); + } + this._escapedAttributes[attr] = escaped; + return escaped; + }, + + /** + * Gets the value of an attribute. + * @param {String} attr The name of an attribute. + */ + get: function(attr) { + return this.attributes[attr]; + }, + + _finishFetch: function(serverData) { + this.attributes = Parse._decode(null, _.clone(serverData.params)); + if (!Parse.Storage.async) { + // We only provide local caching of config with synchronous Storage + Parse.Storage.setItem( + Parse._getParsePath(Parse.Config._CURRENT_CONFIG_KEY), + JSON.stringify(serverData)); + } + } + }; + + Parse.Config._currentConfig = null; + + Parse.Config._CURRENT_CONFIG_KEY = "currentConfig"; + +}(this)); + + +(function(root) { + root.Parse = root.Parse || {}; + var Parse = root.Parse; + var _ = Parse._; + + /** + * Constructs a new Parse.Error object with the given code and message. + * @param {Number} code An error code constant from Parse.Error. + * @param {String} message A detailed description of the error. + * @class + * + *

Class used for all objects passed to error callbacks.

+ */ + Parse.Error = function(code, message) { + this.code = code; + this.message = message; + }; + + _.extend(Parse.Error, /** @lends Parse.Error */ { + /** + * Error code indicating some error other than those enumerated here. + * @constant + */ + OTHER_CAUSE: -1, + + /** + * Error code indicating that something has gone wrong with the server. + * If you get this error code, it is Parse's fault. Contact us at + * https://parse.com/help + * @constant + */ + INTERNAL_SERVER_ERROR: 1, + + /** + * Error code indicating the connection to the Parse servers failed. + * @constant + */ + CONNECTION_FAILED: 100, + + /** + * Error code indicating the specified object doesn't exist. + * @constant + */ + OBJECT_NOT_FOUND: 101, + + /** + * Error code indicating you tried to query with a datatype that doesn't + * support it, like exact matching an array or object. + * @constant + */ + INVALID_QUERY: 102, + + /** + * Error code indicating a missing or invalid classname. Classnames are + * case-sensitive. They must start with a letter, and a-zA-Z0-9_ are the + * only valid characters. + * @constant + */ + INVALID_CLASS_NAME: 103, + + /** + * Error code indicating an unspecified object id. + * @constant + */ + MISSING_OBJECT_ID: 104, + + /** + * Error code indicating an invalid key name. Keys are case-sensitive. They + * must start with a letter, and a-zA-Z0-9_ are the only valid characters. + * @constant + */ + INVALID_KEY_NAME: 105, + + /** + * Error code indicating a malformed pointer. You should not see this unless + * you have been mucking about changing internal Parse code. + * @constant + */ + INVALID_POINTER: 106, + + /** + * Error code indicating that badly formed JSON was received upstream. This + * either indicates you have done something unusual with modifying how + * things encode to JSON, or the network is failing badly. + * @constant + */ + INVALID_JSON: 107, + + /** + * Error code indicating that the feature you tried to access is only + * available internally for testing purposes. + * @constant + */ + COMMAND_UNAVAILABLE: 108, + + /** + * You must call Parse.initialize before using the Parse library. + * @constant + */ + NOT_INITIALIZED: 109, + + /** + * Error code indicating that a field was set to an inconsistent type. + * @constant + */ + INCORRECT_TYPE: 111, + + /** + * Error code indicating an invalid channel name. A channel name is either + * an empty string (the broadcast channel) or contains only a-zA-Z0-9_ + * characters and starts with a letter. + * @constant + */ + INVALID_CHANNEL_NAME: 112, + + /** + * Error code indicating that push is misconfigured. + * @constant + */ + PUSH_MISCONFIGURED: 115, + + /** + * Error code indicating that the object is too large. + * @constant + */ + OBJECT_TOO_LARGE: 116, + + /** + * Error code indicating that the operation isn't allowed for clients. + * @constant + */ + OPERATION_FORBIDDEN: 119, + + /** + * Error code indicating the result was not found in the cache. + * @constant + */ + CACHE_MISS: 120, + + /** + * Error code indicating that an invalid key was used in a nested + * JSONObject. + * @constant + */ + INVALID_NESTED_KEY: 121, + + /** + * Error code indicating that an invalid filename was used for ParseFile. + * A valid file name contains only a-zA-Z0-9_. characters and is between 1 + * and 128 characters. + * @constant + */ + INVALID_FILE_NAME: 122, + + /** + * Error code indicating an invalid ACL was provided. + * @constant + */ + INVALID_ACL: 123, + + /** + * Error code indicating that the request timed out on the server. Typically + * this indicates that the request is too expensive to run. + * @constant + */ + TIMEOUT: 124, + + /** + * Error code indicating that the email address was invalid. + * @constant + */ + INVALID_EMAIL_ADDRESS: 125, + + /** + * Error code indicating a missing content type. + * @constant + */ + MISSING_CONTENT_TYPE: 126, + + /** + * Error code indicating a missing content length. + * @constant + */ + MISSING_CONTENT_LENGTH: 127, + + /** + * Error code indicating an invalid content length. + * @constant + */ + INVALID_CONTENT_LENGTH: 128, + + /** + * Error code indicating a file that was too large. + * @constant + */ + FILE_TOO_LARGE: 129, + + /** + * Error code indicating an error saving a file. + * @constant + */ + FILE_SAVE_ERROR: 130, + + /** + * Error code indicating that a unique field was given a value that is + * already taken. + * @constant + */ + DUPLICATE_VALUE: 137, + + /** + * Error code indicating that a role's name is invalid. + * @constant + */ + INVALID_ROLE_NAME: 139, + + /** + * Error code indicating that an application quota was exceeded. Upgrade to + * resolve. + * @constant + */ + EXCEEDED_QUOTA: 140, + + /** + * Error code indicating that a Cloud Code script failed. + * @constant + */ + SCRIPT_FAILED: 141, + + /** + * Error code indicating that a Cloud Code validation failed. + * @constant + */ + VALIDATION_ERROR: 142, + + /** + * Error code indicating that invalid image data was provided. + * @constant + */ + INVALID_IMAGE_DATA: 150, + + /** + * Error code indicating an unsaved file. + * @constant + */ + UNSAVED_FILE_ERROR: 151, + + /** + * Error code indicating an invalid push time. + */ + INVALID_PUSH_TIME_ERROR: 152, + + /** + * Error code indicating an error deleting a file. + * @constant + */ + FILE_DELETE_ERROR: 153, + + /** + * Error code indicating that the application has exceeded its request + * limit. + * @constant + */ + REQUEST_LIMIT_EXCEEDED: 155, + + /** + * Error code indicating an invalid event name. + */ + INVALID_EVENT_NAME: 160, + + /** + * Error code indicating that the username is missing or empty. + * @constant + */ + USERNAME_MISSING: 200, + + /** + * Error code indicating that the password is missing or empty. + * @constant + */ + PASSWORD_MISSING: 201, + + /** + * Error code indicating that the username has already been taken. + * @constant + */ + USERNAME_TAKEN: 202, + + /** + * Error code indicating that the email has already been taken. + * @constant + */ + EMAIL_TAKEN: 203, + + /** + * Error code indicating that the email is missing, but must be specified. + * @constant + */ + EMAIL_MISSING: 204, + + /** + * Error code indicating that a user with the specified email was not found. + * @constant + */ + EMAIL_NOT_FOUND: 205, + + /** + * Error code indicating that a user object without a valid session could + * not be altered. + * @constant + */ + SESSION_MISSING: 206, + + /** + * Error code indicating that a user can only be created through signup. + * @constant + */ + MUST_CREATE_USER_THROUGH_SIGNUP: 207, + + /** + * Error code indicating that an an account being linked is already linked + * to another user. + * @constant + */ + ACCOUNT_ALREADY_LINKED: 208, + + /** + * Error code indicating that the current session token is invalid. + * @constant + */ + INVALID_SESSION_TOKEN: 209, + + /** + * Error code indicating that a user cannot be linked to an account because + * that account's id could not be found. + * @constant + */ + LINKED_ID_MISSING: 250, + + /** + * Error code indicating that a user with a linked (e.g. Facebook) account + * has an invalid session. + * @constant + */ + INVALID_LINKED_SESSION: 251, + + /** + * Error code indicating that a service being linked (e.g. Facebook or + * Twitter) is unsupported. + * @constant + */ + UNSUPPORTED_SERVICE: 252, + + /** + * Error code indicating that there were multiple errors. Aggregate errors + * have an "errors" property, which is an array of error objects with more + * detail about each error that occurred. + * @constant + */ + AGGREGATE_ERROR: 600, + + /** + * Error code indicating the client was unable to read an input file. + * @constant + */ + FILE_READ_ERROR: 601, + + /** + * Error code indicating a real error code is unavailable because + * we had to use an XDomainRequest object to allow CORS requests in + * Internet Explorer, which strips the body from HTTP responses that have + * a non-2XX status code. + * @constant + */ + X_DOMAIN_REQUEST: 602 + }); + +}(this)); + +/*global _: false */ +(function() { + var root = this; + var Parse = (root.Parse || (root.Parse = {})); + var eventSplitter = /\s+/; + var slice = Array.prototype.slice; + + /** + * @class + * + *

Parse.Events is a fork of Backbone's Events module, provided for your + * convenience.

+ * + *

A module that can be mixed in to any object in order to provide + * it with custom events. You may bind callback functions to an event + * with `on`, or remove these functions with `off`. + * Triggering an event fires all callbacks in the order that `on` was + * called. + * + *

+   *     var object = {};
+   *     _.extend(object, Parse.Events);
+   *     object.on('expand', function(){ alert('expanded'); });
+   *     object.trigger('expand');

+ * + *

For more information, see the + * Backbone + * documentation.

+ */ + Parse.Events = { + /** + * Bind one or more space separated events, `events`, to a `callback` + * function. Passing `"all"` will bind the callback to all events fired. + */ + on: function(events, callback, context) { + + var calls, event, node, tail, list; + if (!callback) { + return this; + } + events = events.split(eventSplitter); + calls = this._callbacks || (this._callbacks = {}); + + // Create an immutable callback list, allowing traversal during + // modification. The tail is an empty object that will always be used + // as the next node. + event = events.shift(); + while (event) { + list = calls[event]; + node = list ? list.tail : {}; + node.next = tail = {}; + node.context = context; + node.callback = callback; + calls[event] = {tail: tail, next: list ? list.next : node}; + event = events.shift(); + } + + return this; + }, + + /** + * Remove one or many callbacks. If `context` is null, removes all callbacks + * with that function. If `callback` is null, removes all callbacks for the + * event. If `events` is null, removes all bound callbacks for all events. + */ + off: function(events, callback, context) { + var event, calls, node, tail, cb, ctx; + + // No events, or removing *all* events. + if (!(calls = this._callbacks)) { + return; + } + if (!(events || callback || context)) { + delete this._callbacks; + return this; + } + + // Loop through the listed events and contexts, splicing them out of the + // linked list of callbacks if appropriate. + events = events ? events.split(eventSplitter) : Object.keys(calls); + event = events.shift(); + while (event) { + node = calls[event]; + delete calls[event]; + if (!node || !(callback || context)) { + event = events.shift(); + continue; + } + // Create a new list, omitting the indicated callbacks. + tail = node.tail; + node = node.next; + while (node !== tail) { + cb = node.callback; + ctx = node.context; + if ((callback && cb !== callback) || (context && ctx !== context)) { + this.on(event, cb, ctx); + } + node = node.next; + } + event = events.shift(); + } + + return this; + }, + + /** + * Trigger one or many events, firing all bound callbacks. Callbacks are + * passed the same arguments as `trigger` is, apart from the event name + * (unless you're listening on `"all"`, which will cause your callback to + * receive the true name of the event as the first argument). + */ + trigger: function(events) { + var event, node, calls, tail, args, all, rest; + if (!(calls = this._callbacks)) { + return this; + } + all = calls.all; + events = events.split(eventSplitter); + rest = slice.call(arguments, 1); + + // For each event, walk through the linked list of callbacks twice, + // first to trigger the event, then to trigger any `"all"` callbacks. + event = events.shift(); + while (event) { + node = calls[event]; + if (node) { + tail = node.tail; + while ((node = node.next) !== tail) { + node.callback.apply(node.context || this, rest); + } + } + node = all; + if (node) { + tail = node.tail; + args = [event].concat(rest); + while ((node = node.next) !== tail) { + node.callback.apply(node.context || this, args); + } + } + event = events.shift(); + } + + return this; + } + }; + + /** + * @function + */ + Parse.Events.bind = Parse.Events.on; + + /** + * @function + */ + Parse.Events.unbind = Parse.Events.off; +}.call(this)); + + +/*global navigator: false */ +(function(root) { + root.Parse = root.Parse || {}; + var Parse = root.Parse; + var _ = Parse._; + + /** + * Creates a new GeoPoint with any of the following forms:
+ *
+   *   new GeoPoint(otherGeoPoint)
+   *   new GeoPoint(30, 30)
+   *   new GeoPoint([30, 30])
+   *   new GeoPoint({latitude: 30, longitude: 30})
+   *   new GeoPoint()  // defaults to (0, 0)
+   *   
+ * @class + * + *

Represents a latitude / longitude point that may be associated + * with a key in a ParseObject or used as a reference point for geo queries. + * This allows proximity-based queries on the key.

+ * + *

Only one key in a class may contain a GeoPoint.

+ * + *

Example:

+   *   var point = new Parse.GeoPoint(30.0, -20.0);
+   *   var object = new Parse.Object("PlaceObject");
+   *   object.set("location", point);
+   *   object.save();

+ */ + Parse.GeoPoint = function(arg1, arg2) { + if (_.isArray(arg1)) { + Parse.GeoPoint._validate(arg1[0], arg1[1]); + this.latitude = arg1[0]; + this.longitude = arg1[1]; + } else if (_.isObject(arg1)) { + Parse.GeoPoint._validate(arg1.latitude, arg1.longitude); + this.latitude = arg1.latitude; + this.longitude = arg1.longitude; + } else if (_.isNumber(arg1) && _.isNumber(arg2)) { + Parse.GeoPoint._validate(arg1, arg2); + this.latitude = arg1; + this.longitude = arg2; + } else { + this.latitude = 0; + this.longitude = 0; + } + + // Add properties so that anyone using Webkit or Mozilla will get an error + // if they try to set values that are out of bounds. + var self = this; + if (this.__defineGetter__ && this.__defineSetter__) { + // Use _latitude and _longitude to actually store the values, and add + // getters and setters for latitude and longitude. + this._latitude = this.latitude; + this._longitude = this.longitude; + this.__defineGetter__("latitude", function() { + return self._latitude; + }); + this.__defineGetter__("longitude", function() { + return self._longitude; + }); + this.__defineSetter__("latitude", function(val) { + Parse.GeoPoint._validate(val, self.longitude); + self._latitude = val; + }); + this.__defineSetter__("longitude", function(val) { + Parse.GeoPoint._validate(self.latitude, val); + self._longitude = val; + }); + } + }; + + /** + * @lends Parse.GeoPoint.prototype + * @property {float} latitude North-south portion of the coordinate, in range + * [-90, 90]. Throws an exception if set out of range in a modern browser. + * @property {float} longitude East-west portion of the coordinate, in range + * [-180, 180]. Throws if set out of range in a modern browser. + */ + + /** + * Throws an exception if the given lat-long is out of bounds. + */ + Parse.GeoPoint._validate = function(latitude, longitude) { + if (latitude < -90.0) { + throw "Parse.GeoPoint latitude " + latitude + " < -90.0."; + } + if (latitude > 90.0) { + throw "Parse.GeoPoint latitude " + latitude + " > 90.0."; + } + if (longitude < -180.0) { + throw "Parse.GeoPoint longitude " + longitude + " < -180.0."; + } + if (longitude > 180.0) { + throw "Parse.GeoPoint longitude " + longitude + " > 180.0."; + } + }; + + /** + * Creates a GeoPoint with the user's current location, if available. + * Calls options.success with a new GeoPoint instance or calls options.error. + * @param {Object} options An object with success and error callbacks. + */ + Parse.GeoPoint.current = function(options) { + var promise = new Parse.Promise(); + navigator.geolocation.getCurrentPosition(function(location) { + promise.resolve(new Parse.GeoPoint({ + latitude: location.coords.latitude, + longitude: location.coords.longitude + })); + + }, function(error) { + promise.reject(error); + }); + + return promise._thenRunCallbacks(options); + }; + + Parse.GeoPoint.prototype = { + /** + * Returns a JSON representation of the GeoPoint, suitable for Parse. + * @return {Object} + */ + toJSON: function() { + Parse.GeoPoint._validate(this.latitude, this.longitude); + return { + "__type": "GeoPoint", + latitude: this.latitude, + longitude: this.longitude + }; + }, + + /** + * Returns the distance from this GeoPoint to another in radians. + * @param {Parse.GeoPoint} point the other Parse.GeoPoint. + * @return {Number} + */ + radiansTo: function(point) { + var d2r = Math.PI / 180.0; + var lat1rad = this.latitude * d2r; + var long1rad = this.longitude * d2r; + var lat2rad = point.latitude * d2r; + var long2rad = point.longitude * d2r; + var deltaLat = lat1rad - lat2rad; + var deltaLong = long1rad - long2rad; + var sinDeltaLatDiv2 = Math.sin(deltaLat / 2); + var sinDeltaLongDiv2 = Math.sin(deltaLong / 2); + // Square of half the straight line chord distance between both points. + var a = ((sinDeltaLatDiv2 * sinDeltaLatDiv2) + + (Math.cos(lat1rad) * Math.cos(lat2rad) * + sinDeltaLongDiv2 * sinDeltaLongDiv2)); + a = Math.min(1.0, a); + return 2 * Math.asin(Math.sqrt(a)); + }, + + /** + * Returns the distance from this GeoPoint to another in kilometers. + * @param {Parse.GeoPoint} point the other Parse.GeoPoint. + * @return {Number} + */ + kilometersTo: function(point) { + return this.radiansTo(point) * 6371.0; + }, + + /** + * Returns the distance from this GeoPoint to another in miles. + * @param {Parse.GeoPoint} point the other Parse.GeoPoint. + * @return {Number} + */ + milesTo: function(point) { + return this.radiansTo(point) * 3958.8; + } + }; +}(this)); + +/*global navigator: false */ +(function(root) { + root.Parse = root.Parse || {}; + var Parse = root.Parse; + var _ = Parse._; + + var PUBLIC_KEY = "*"; + + /** + * Creates a new ACL. + * If no argument is given, the ACL has no permissions for anyone. + * If the argument is a Parse.User, the ACL will have read and write + * permission for only that user. + * If the argument is any other JSON object, that object will be interpretted + * as a serialized ACL created with toJSON(). + * @see Parse.Object#setACL + * @class + * + *

An ACL, or Access Control List can be added to any + * Parse.Object to restrict access to only a subset of users + * of your application.

+ */ + Parse.ACL = function(arg1) { + var self = this; + self.permissionsById = {}; + if (_.isObject(arg1)) { + if (arg1 instanceof Parse.User) { + self.setReadAccess(arg1, true); + self.setWriteAccess(arg1, true); + } else { + if (_.isFunction(arg1)) { + throw "Parse.ACL() called with a function. Did you forget ()?"; + } + Parse._objectEach(arg1, function(accessList, userId) { + if (!_.isString(userId)) { + throw "Tried to create an ACL with an invalid userId."; + } + self.permissionsById[userId] = {}; + Parse._objectEach(accessList, function(allowed, permission) { + if (permission !== "read" && permission !== "write") { + throw "Tried to create an ACL with an invalid permission type."; + } + if (!_.isBoolean(allowed)) { + throw "Tried to create an ACL with an invalid permission value."; + } + self.permissionsById[userId][permission] = allowed; + }); + }); + } + } + }; + + /** + * Returns a JSON-encoded version of the ACL. + * @return {Object} + */ + Parse.ACL.prototype.toJSON = function() { + return _.clone(this.permissionsById); + }; + + Parse.ACL.prototype._setAccess = function(accessType, userId, allowed) { + if (userId instanceof Parse.User) { + userId = userId.id; + } else if (userId instanceof Parse.Role) { + userId = "role:" + userId.getName(); + } + if (!_.isString(userId)) { + throw "userId must be a string."; + } + if (!_.isBoolean(allowed)) { + throw "allowed must be either true or false."; + } + var permissions = this.permissionsById[userId]; + if (!permissions) { + if (!allowed) { + // The user already doesn't have this permission, so no action needed. + return; + } else { + permissions = {}; + this.permissionsById[userId] = permissions; + } + } + + if (allowed) { + this.permissionsById[userId][accessType] = true; + } else { + delete permissions[accessType]; + if (_.isEmpty(permissions)) { + delete permissions[userId]; + } + } + }; + + Parse.ACL.prototype._getAccess = function(accessType, userId) { + if (userId instanceof Parse.User) { + userId = userId.id; + } else if (userId instanceof Parse.Role) { + userId = "role:" + userId.getName(); + } + var permissions = this.permissionsById[userId]; + if (!permissions) { + return false; + } + return permissions[accessType] ? true : false; + }; + + /** + * Set whether the given user is allowed to read this object. + * @param userId An instance of Parse.User or its objectId. + * @param {Boolean} allowed Whether that user should have read access. + */ + Parse.ACL.prototype.setReadAccess = function(userId, allowed) { + this._setAccess("read", userId, allowed); + }; + + /** + * Get whether the given user id is *explicitly* allowed to read this object. + * Even if this returns false, the user may still be able to access it if + * getPublicReadAccess returns true or a role that the user belongs to has + * write access. + * @param userId An instance of Parse.User or its objectId, or a Parse.Role. + * @return {Boolean} + */ + Parse.ACL.prototype.getReadAccess = function(userId) { + return this._getAccess("read", userId); + }; + + /** + * Set whether the given user id is allowed to write this object. + * @param userId An instance of Parse.User or its objectId, or a Parse.Role.. + * @param {Boolean} allowed Whether that user should have write access. + */ + Parse.ACL.prototype.setWriteAccess = function(userId, allowed) { + this._setAccess("write", userId, allowed); + }; + + /** + * Get whether the given user id is *explicitly* allowed to write this object. + * Even if this returns false, the user may still be able to write it if + * getPublicWriteAccess returns true or a role that the user belongs to has + * write access. + * @param userId An instance of Parse.User or its objectId, or a Parse.Role. + * @return {Boolean} + */ + Parse.ACL.prototype.getWriteAccess = function(userId) { + return this._getAccess("write", userId); + }; + + /** + * Set whether the public is allowed to read this object. + * @param {Boolean} allowed + */ + Parse.ACL.prototype.setPublicReadAccess = function(allowed) { + this.setReadAccess(PUBLIC_KEY, allowed); + }; + + /** + * Get whether the public is allowed to read this object. + * @return {Boolean} + */ + Parse.ACL.prototype.getPublicReadAccess = function() { + return this.getReadAccess(PUBLIC_KEY); + }; + + /** + * Set whether the public is allowed to write this object. + * @param {Boolean} allowed + */ + Parse.ACL.prototype.setPublicWriteAccess = function(allowed) { + this.setWriteAccess(PUBLIC_KEY, allowed); + }; + + /** + * Get whether the public is allowed to write this object. + * @return {Boolean} + */ + Parse.ACL.prototype.getPublicWriteAccess = function() { + return this.getWriteAccess(PUBLIC_KEY); + }; + + /** + * Get whether users belonging to the given role are allowed + * to read this object. Even if this returns false, the role may + * still be able to write it if a parent role has read access. + * + * @param role The name of the role, or a Parse.Role object. + * @return {Boolean} true if the role has read access. false otherwise. + * @throws {String} If role is neither a Parse.Role nor a String. + */ + Parse.ACL.prototype.getRoleReadAccess = function(role) { + if (role instanceof Parse.Role) { + // Normalize to the String name + role = role.getName(); + } + if (_.isString(role)) { + return this.getReadAccess("role:" + role); + } + throw "role must be a Parse.Role or a String"; + }; + + /** + * Get whether users belonging to the given role are allowed + * to write this object. Even if this returns false, the role may + * still be able to write it if a parent role has write access. + * + * @param role The name of the role, or a Parse.Role object. + * @return {Boolean} true if the role has write access. false otherwise. + * @throws {String} If role is neither a Parse.Role nor a String. + */ + Parse.ACL.prototype.getRoleWriteAccess = function(role) { + if (role instanceof Parse.Role) { + // Normalize to the String name + role = role.getName(); + } + if (_.isString(role)) { + return this.getWriteAccess("role:" + role); + } + throw "role must be a Parse.Role or a String"; + }; + + /** + * Set whether users belonging to the given role are allowed + * to read this object. + * + * @param role The name of the role, or a Parse.Role object. + * @param {Boolean} allowed Whether the given role can read this object. + * @throws {String} If role is neither a Parse.Role nor a String. + */ + Parse.ACL.prototype.setRoleReadAccess = function(role, allowed) { + if (role instanceof Parse.Role) { + // Normalize to the String name + role = role.getName(); + } + if (_.isString(role)) { + this.setReadAccess("role:" + role, allowed); + return; + } + throw "role must be a Parse.Role or a String"; + }; + + /** + * Set whether users belonging to the given role are allowed + * to write this object. + * + * @param role The name of the role, or a Parse.Role object. + * @param {Boolean} allowed Whether the given role can write this object. + * @throws {String} If role is neither a Parse.Role nor a String. + */ + Parse.ACL.prototype.setRoleWriteAccess = function(role, allowed) { + if (role instanceof Parse.Role) { + // Normalize to the String name + role = role.getName(); + } + if (_.isString(role)) { + this.setWriteAccess("role:" + role, allowed); + return; + } + throw "role must be a Parse.Role or a String"; + }; + +}(this)); + +(function(root) { + root.Parse = root.Parse || {}; + var Parse = root.Parse; + var _ = Parse._; + + /** + * @class + * A Parse.Op is an atomic operation that can be applied to a field in a + * Parse.Object. For example, calling object.set("foo", "bar") + * is an example of a Parse.Op.Set. Calling object.unset("foo") + * is a Parse.Op.Unset. These operations are stored in a Parse.Object and + * sent to the server as part of object.save() operations. + * Instances of Parse.Op should be immutable. + * + * You should not create subclasses of Parse.Op or instantiate Parse.Op + * directly. + */ + Parse.Op = function() { + this._initialize.apply(this, arguments); + }; + + Parse.Op.prototype = { + _initialize: function() {} + }; + + _.extend(Parse.Op, { + /** + * To create a new Op, call Parse.Op._extend(); + */ + _extend: Parse._extend, + + // A map of __op string to decoder function. + _opDecoderMap: {}, + + /** + * Registers a function to convert a json object with an __op field into an + * instance of a subclass of Parse.Op. + */ + _registerDecoder: function(opName, decoder) { + Parse.Op._opDecoderMap[opName] = decoder; + }, + + /** + * Converts a json object into an instance of a subclass of Parse.Op. + */ + _decode: function(json) { + var decoder = Parse.Op._opDecoderMap[json.__op]; + if (decoder) { + return decoder(json); + } else { + return undefined; + } + } + }); + + /* + * Add a handler for Batch ops. + */ + Parse.Op._registerDecoder("Batch", function(json) { + var op = null; + Parse._arrayEach(json.ops, function(nextOp) { + nextOp = Parse.Op._decode(nextOp); + op = nextOp._mergeWithPrevious(op); + }); + return op; + }); + + /** + * @class + * A Set operation indicates that either the field was changed using + * Parse.Object.set, or it is a mutable container that was detected as being + * changed. + */ + Parse.Op.Set = Parse.Op._extend(/** @lends Parse.Op.Set.prototype */ { + _initialize: function(value) { + this._value = value; + }, + + /** + * Returns the new value of this field after the set. + */ + value: function() { + return this._value; + }, + + /** + * Returns a JSON version of the operation suitable for sending to Parse. + * @return {Object} + */ + toJSON: function() { + return Parse._encode(this.value()); + }, + + _mergeWithPrevious: function(previous) { + return this; + }, + + _estimate: function(oldValue) { + return this.value(); + } + }); + + /** + * A sentinel value that is returned by Parse.Op.Unset._estimate to + * indicate the field should be deleted. Basically, if you find _UNSET as a + * value in your object, you should remove that key. + */ + Parse.Op._UNSET = {}; + + /** + * @class + * An Unset operation indicates that this field has been deleted from the + * object. + */ + Parse.Op.Unset = Parse.Op._extend(/** @lends Parse.Op.Unset.prototype */ { + /** + * Returns a JSON version of the operation suitable for sending to Parse. + * @return {Object} + */ + toJSON: function() { + return { __op: "Delete" }; + }, + + _mergeWithPrevious: function(previous) { + return this; + }, + + _estimate: function(oldValue) { + return Parse.Op._UNSET; + } + }); + + Parse.Op._registerDecoder("Delete", function(json) { + return new Parse.Op.Unset(); + }); + + /** + * @class + * An Increment is an atomic operation where the numeric value for the field + * will be increased by a given amount. + */ + Parse.Op.Increment = Parse.Op._extend( + /** @lends Parse.Op.Increment.prototype */ { + + _initialize: function(amount) { + this._amount = amount; + }, + + /** + * Returns the amount to increment by. + * @return {Number} the amount to increment by. + */ + amount: function() { + return this._amount; + }, + + /** + * Returns a JSON version of the operation suitable for sending to Parse. + * @return {Object} + */ + toJSON: function() { + return { __op: "Increment", amount: this._amount }; + }, + + _mergeWithPrevious: function(previous) { + if (!previous) { + return this; + } else if (previous instanceof Parse.Op.Unset) { + return new Parse.Op.Set(this.amount()); + } else if (previous instanceof Parse.Op.Set) { + return new Parse.Op.Set(previous.value() + this.amount()); + } else if (previous instanceof Parse.Op.Increment) { + return new Parse.Op.Increment(this.amount() + previous.amount()); + } else { + throw "Op is invalid after previous op."; + } + }, + + _estimate: function(oldValue) { + if (!oldValue) { + return this.amount(); + } + return oldValue + this.amount(); + } + }); + + Parse.Op._registerDecoder("Increment", function(json) { + return new Parse.Op.Increment(json.amount); + }); + + /** + * @class + * Add is an atomic operation where the given objects will be appended to the + * array that is stored in this field. + */ + Parse.Op.Add = Parse.Op._extend(/** @lends Parse.Op.Add.prototype */ { + _initialize: function(objects) { + this._objects = objects; + }, + + /** + * Returns the objects to be added to the array. + * @return {Array} The objects to be added to the array. + */ + objects: function() { + return this._objects; + }, + + /** + * Returns a JSON version of the operation suitable for sending to Parse. + * @return {Object} + */ + toJSON: function() { + return { __op: "Add", objects: Parse._encode(this.objects()) }; + }, + + _mergeWithPrevious: function(previous) { + if (!previous) { + return this; + } else if (previous instanceof Parse.Op.Unset) { + return new Parse.Op.Set(this.objects()); + } else if (previous instanceof Parse.Op.Set) { + return new Parse.Op.Set(this._estimate(previous.value())); + } else if (previous instanceof Parse.Op.Add) { + return new Parse.Op.Add(previous.objects().concat(this.objects())); + } else { + throw "Op is invalid after previous op."; + } + }, + + _estimate: function(oldValue) { + if (!oldValue) { + return _.clone(this.objects()); + } else { + return oldValue.concat(this.objects()); + } + } + }); + + Parse.Op._registerDecoder("Add", function(json) { + return new Parse.Op.Add(Parse._decode(undefined, json.objects)); + }); + + /** + * @class + * AddUnique is an atomic operation where the given items will be appended to + * the array that is stored in this field only if they were not already + * present in the array. + */ + Parse.Op.AddUnique = Parse.Op._extend( + /** @lends Parse.Op.AddUnique.prototype */ { + + _initialize: function(objects) { + this._objects = _.uniq(objects); + }, + + /** + * Returns the objects to be added to the array. + * @return {Array} The objects to be added to the array. + */ + objects: function() { + return this._objects; + }, + + /** + * Returns a JSON version of the operation suitable for sending to Parse. + * @return {Object} + */ + toJSON: function() { + return { __op: "AddUnique", objects: Parse._encode(this.objects()) }; + }, + + _mergeWithPrevious: function(previous) { + if (!previous) { + return this; + } else if (previous instanceof Parse.Op.Unset) { + return new Parse.Op.Set(this.objects()); + } else if (previous instanceof Parse.Op.Set) { + return new Parse.Op.Set(this._estimate(previous.value())); + } else if (previous instanceof Parse.Op.AddUnique) { + return new Parse.Op.AddUnique(this._estimate(previous.objects())); + } else { + throw "Op is invalid after previous op."; + } + }, + + _estimate: function(oldValue) { + if (!oldValue) { + return _.clone(this.objects()); + } else { + // We can't just take the _.uniq(_.union(...)) of oldValue and + // this.objects, because the uniqueness may not apply to oldValue + // (especially if the oldValue was set via .set()) + var newValue = _.clone(oldValue); + Parse._arrayEach(this.objects(), function(obj) { + if (obj instanceof Parse.Object && obj.id) { + var matchingObj = _.find(newValue, function(anObj) { + return (anObj instanceof Parse.Object) && (anObj.id === obj.id); + }); + if (!matchingObj) { + newValue.push(obj); + } else { + var index = _.indexOf(newValue, matchingObj); + newValue[index] = obj; + } + } else if (!_.contains(newValue, obj)) { + newValue.push(obj); + } + }); + return newValue; + } + } + }); + + Parse.Op._registerDecoder("AddUnique", function(json) { + return new Parse.Op.AddUnique(Parse._decode(undefined, json.objects)); + }); + + /** + * @class + * Remove is an atomic operation where the given objects will be removed from + * the array that is stored in this field. + */ + Parse.Op.Remove = Parse.Op._extend(/** @lends Parse.Op.Remove.prototype */ { + _initialize: function(objects) { + this._objects = _.uniq(objects); + }, + + /** + * Returns the objects to be removed from the array. + * @return {Array} The objects to be removed from the array. + */ + objects: function() { + return this._objects; + }, + + /** + * Returns a JSON version of the operation suitable for sending to Parse. + * @return {Object} + */ + toJSON: function() { + return { __op: "Remove", objects: Parse._encode(this.objects()) }; + }, + + _mergeWithPrevious: function(previous) { + if (!previous) { + return this; + } else if (previous instanceof Parse.Op.Unset) { + return previous; + } else if (previous instanceof Parse.Op.Set) { + return new Parse.Op.Set(this._estimate(previous.value())); + } else if (previous instanceof Parse.Op.Remove) { + return new Parse.Op.Remove(_.union(previous.objects(), this.objects())); + } else { + throw "Op is invalid after previous op."; + } + }, + + _estimate: function(oldValue) { + if (!oldValue) { + return []; + } else { + var newValue = _.difference(oldValue, this.objects()); + // If there are saved Parse Objects being removed, also remove them. + Parse._arrayEach(this.objects(), function(obj) { + if (obj instanceof Parse.Object && obj.id) { + newValue = _.reject(newValue, function(other) { + return (other instanceof Parse.Object) && (other.id === obj.id); + }); + } + }); + return newValue; + } + } + }); + + Parse.Op._registerDecoder("Remove", function(json) { + return new Parse.Op.Remove(Parse._decode(undefined, json.objects)); + }); + + /** + * @class + * A Relation operation indicates that the field is an instance of + * Parse.Relation, and objects are being added to, or removed from, that + * relation. + */ + Parse.Op.Relation = Parse.Op._extend( + /** @lends Parse.Op.Relation.prototype */ { + + _initialize: function(adds, removes) { + this._targetClassName = null; + + var self = this; + + var pointerToId = function(object) { + if (object instanceof Parse.Object) { + if (!object.id) { + throw "You can't add an unsaved Parse.Object to a relation."; + } + if (!self._targetClassName) { + self._targetClassName = object.className; + } + if (self._targetClassName !== object.className) { + throw "Tried to create a Parse.Relation with 2 different types: " + + self._targetClassName + " and " + object.className + "."; + } + return object.id; + } + return object; + }; + + this.relationsToAdd = _.uniq(_.map(adds, pointerToId)); + this.relationsToRemove = _.uniq(_.map(removes, pointerToId)); + }, + + /** + * Returns an array of unfetched Parse.Object that are being added to the + * relation. + * @return {Array} + */ + added: function() { + var self = this; + return _.map(this.relationsToAdd, function(objectId) { + var object = Parse.Object._create(self._targetClassName); + object.id = objectId; + return object; + }); + }, + + /** + * Returns an array of unfetched Parse.Object that are being removed from + * the relation. + * @return {Array} + */ + removed: function() { + var self = this; + return _.map(this.relationsToRemove, function(objectId) { + var object = Parse.Object._create(self._targetClassName); + object.id = objectId; + return object; + }); + }, + + /** + * Returns a JSON version of the operation suitable for sending to Parse. + * @return {Object} + */ + toJSON: function() { + var adds = null; + var removes = null; + var self = this; + var idToPointer = function(id) { + return { __type: 'Pointer', + className: self._targetClassName, + objectId: id }; + }; + var pointers = null; + if (this.relationsToAdd.length > 0) { + pointers = _.map(this.relationsToAdd, idToPointer); + adds = { "__op": "AddRelation", "objects": pointers }; + } + + if (this.relationsToRemove.length > 0) { + pointers = _.map(this.relationsToRemove, idToPointer); + removes = { "__op": "RemoveRelation", "objects": pointers }; + } + + if (adds && removes) { + return { "__op": "Batch", "ops": [adds, removes]}; + } + + return adds || removes || {}; + }, + + _mergeWithPrevious: function(previous) { + if (!previous) { + return this; + } else if (previous instanceof Parse.Op.Unset) { + throw "You can't modify a relation after deleting it."; + } else if (previous instanceof Parse.Op.Relation) { + if (previous._targetClassName && + previous._targetClassName !== this._targetClassName) { + throw "Related object must be of class " + previous._targetClassName + + ", but " + this._targetClassName + " was passed in."; + } + var newAdd = _.union(_.difference(previous.relationsToAdd, + this.relationsToRemove), + this.relationsToAdd); + var newRemove = _.union(_.difference(previous.relationsToRemove, + this.relationsToAdd), + this.relationsToRemove); + + var newRelation = new Parse.Op.Relation(newAdd, newRemove); + newRelation._targetClassName = this._targetClassName; + return newRelation; + } else { + throw "Op is invalid after previous op."; + } + }, + + _estimate: function(oldValue, object, key) { + if (!oldValue) { + var relation = new Parse.Relation(object, key); + relation.targetClassName = this._targetClassName; + } else if (oldValue instanceof Parse.Relation) { + if (this._targetClassName) { + if (oldValue.targetClassName) { + if (oldValue.targetClassName !== this._targetClassName) { + throw "Related object must be a " + oldValue.targetClassName + + ", but a " + this._targetClassName + " was passed in."; + } + } else { + oldValue.targetClassName = this._targetClassName; + } + } + return oldValue; + } else { + throw "Op is invalid after previous op."; + } + } + }); + + Parse.Op._registerDecoder("AddRelation", function(json) { + return new Parse.Op.Relation(Parse._decode(undefined, json.objects), []); + }); + Parse.Op._registerDecoder("RemoveRelation", function(json) { + return new Parse.Op.Relation([], Parse._decode(undefined, json.objects)); + }); + +}(this)); + +(function(root) { + root.Parse = root.Parse || {}; + var Parse = root.Parse; + var _ = Parse._; + + /** + * Creates a new Relation for the given parent object and key. This + * constructor should rarely be used directly, but rather created by + * Parse.Object.relation. + * @param {Parse.Object} parent The parent of this relation. + * @param {String} key The key for this relation on the parent. + * @see Parse.Object#relation + * @class + * + *

+ * A class that is used to access all of the children of a many-to-many + * relationship. Each instance of Parse.Relation is associated with a + * particular parent object and key. + *

+ */ + Parse.Relation = function(parent, key) { + this.parent = parent; + this.key = key; + this.targetClassName = null; + }; + + Parse.Relation.prototype = { + /** + * Makes sure that this relation has the right parent and key. + */ + _ensureParentAndKey: function(parent, key) { + this.parent = this.parent || parent; + this.key = this.key || key; + if (this.parent !== parent) { + throw "Internal Error. Relation retrieved from two different Objects."; + } + if (this.key !== key) { + throw "Internal Error. Relation retrieved from two different keys."; + } + }, + + /** + * Adds a Parse.Object or an array of Parse.Objects to the relation. + * @param {} objects The item or items to add. + */ + add: function(objects) { + if (!_.isArray(objects)) { + objects = [objects]; + } + + var change = new Parse.Op.Relation(objects, []); + this.parent.set(this.key, change); + this.targetClassName = change._targetClassName; + }, + + /** + * Removes a Parse.Object or an array of Parse.Objects from this relation. + * @param {} objects The item or items to remove. + */ + remove: function(objects) { + if (!_.isArray(objects)) { + objects = [objects]; + } + + var change = new Parse.Op.Relation([], objects); + this.parent.set(this.key, change); + this.targetClassName = change._targetClassName; + }, + + /** + * Returns a JSON version of the object suitable for saving to disk. + * @return {Object} + */ + toJSON: function() { + return { "__type": "Relation", "className": this.targetClassName }; + }, + + /** + * Returns a Parse.Query that is limited to objects in this + * relation. + * @return {Parse.Query} + */ + query: function() { + var targetClass; + var query; + if (!this.targetClassName) { + targetClass = Parse.Object._getSubclass(this.parent.className); + query = new Parse.Query(targetClass); + query._extraOptions.redirectClassNameForKey = this.key; + } else { + targetClass = Parse.Object._getSubclass(this.targetClassName); + query = new Parse.Query(targetClass); + } + query._addCondition("$relatedTo", "object", this.parent._toPointer()); + query._addCondition("$relatedTo", "key", this.key); + + return query; + } + }; +}(this)); + +/*global window: false, process: false */ +(function(root) { + root.Parse = root.Parse || {}; + var Parse = root.Parse; + var _ = Parse._; + + /** + * A Promise is returned by async methods as a hook to provide callbacks to be + * called when the async task is fulfilled. + * + *

Typical usage would be like:

+   *    query.find().then(function(results) {
+   *      results[0].set("foo", "bar");
+   *      return results[0].saveAsync();
+   *    }).then(function(result) {
+   *      console.log("Updated " + result.id);
+   *    });
+   * 

+ * + * @see Parse.Promise.prototype.then + * @class + */ + Parse.Promise = function() { + this._resolved = false; + this._rejected = false; + this._resolvedCallbacks = []; + this._rejectedCallbacks = []; + }; + + _.extend(Parse.Promise, /** @lends Parse.Promise */ { + + _isPromisesAPlusCompliant: false, + + /** + * Returns true iff the given object fulfils the Promise interface. + * @return {Boolean} + */ + is: function(promise) { + return promise && promise.then && _.isFunction(promise.then); + }, + + /** + * Returns a new promise that is resolved with a given value. + * @return {Parse.Promise} the new promise. + */ + as: function() { + var promise = new Parse.Promise(); + promise.resolve.apply(promise, arguments); + return promise; + }, + + /** + * Returns a new promise that is rejected with a given error. + * @return {Parse.Promise} the new promise. + */ + error: function() { + var promise = new Parse.Promise(); + promise.reject.apply(promise, arguments); + return promise; + }, + + /** + * Returns a new promise that is fulfilled when all of the input promises + * are resolved. If any promise in the list fails, then the returned promise + * will fail with the last error. If they all succeed, then the returned + * promise will succeed, with the results being the results of all the input + * promises. For example:
+     *   var p1 = Parse.Promise.as(1);
+     *   var p2 = Parse.Promise.as(2);
+     *   var p3 = Parse.Promise.as(3);
+     *
+     *   Parse.Promise.when(p1, p2, p3).then(function(r1, r2, r3) {
+     *     console.log(r1);  // prints 1
+     *     console.log(r2);  // prints 2
+     *     console.log(r3);  // prints 3
+     *   });
+ * + * The input promises can also be specified as an array:
+     *   var promises = [p1, p2, p3];
+     *   Parse.Promise.when(promises).then(function(r1, r2, r3) {
+     *     console.log(r1);  // prints 1
+     *     console.log(r2);  // prints 2
+     *     console.log(r3);  // prints 3
+     *   });
+     * 
+ * @param {Array} promises a list of promises to wait for. + * @return {Parse.Promise} the new promise. + */ + when: function(promises) { + // Allow passing in Promises as separate arguments instead of an Array. + var objects; + if (promises && Parse._isNullOrUndefined(promises.length)) { + objects = arguments; + } else { + objects = promises; + } + + var total = objects.length; + var hadError = false; + var results = []; + var errors = []; + results.length = objects.length; + errors.length = objects.length; + + if (total === 0) { + return Parse.Promise.as.apply(this, results); + } + + var promise = new Parse.Promise(); + + var resolveOne = function() { + total = total - 1; + if (total === 0) { + if (hadError) { + promise.reject(errors); + } else { + promise.resolve.apply(promise, results); + } + } + }; + + Parse._arrayEach(objects, function(object, i) { + if (Parse.Promise.is(object)) { + object.then(function(result) { + results[i] = result; + resolveOne(); + }, function(error) { + errors[i] = error; + hadError = true; + resolveOne(); + }); + } else { + results[i] = object; + resolveOne(); + } + }); + + return promise; + }, + + /** + * Runs the given asyncFunction repeatedly, as long as the predicate + * function returns a truthy value. Stops repeating if asyncFunction returns + * a rejected promise. + * @param {Function} predicate should return false when ready to stop. + * @param {Function} asyncFunction should return a Promise. + */ + _continueWhile: function(predicate, asyncFunction) { + if (predicate()) { + return asyncFunction().then(function() { + return Parse.Promise._continueWhile(predicate, asyncFunction); + }); + } + return Parse.Promise.as(); + } + }); + + _.extend(Parse.Promise.prototype, /** @lends Parse.Promise.prototype */ { + + /** + * Marks this promise as fulfilled, firing any callbacks waiting on it. + * @param {Object} result the result to pass to the callbacks. + */ + resolve: function(result) { + if (this._resolved || this._rejected) { + throw "A promise was resolved even though it had already been " + + (this._resolved ? "resolved" : "rejected") + "."; + } + this._resolved = true; + this._result = arguments; + var results = arguments; + Parse._arrayEach(this._resolvedCallbacks, function(resolvedCallback) { + resolvedCallback.apply(this, results); + }); + this._resolvedCallbacks = []; + this._rejectedCallbacks = []; + }, + + /** + * Marks this promise as fulfilled, firing any callbacks waiting on it. + * @param {Object} error the error to pass to the callbacks. + */ + reject: function(error) { + if (this._resolved || this._rejected) { + throw "A promise was rejected even though it had already been " + + (this._resolved ? "resolved" : "rejected") + "."; + } + this._rejected = true; + this._error = error; + Parse._arrayEach(this._rejectedCallbacks, function(rejectedCallback) { + rejectedCallback(error); + }); + this._resolvedCallbacks = []; + this._rejectedCallbacks = []; + }, + + /** + * Adds callbacks to be called when this promise is fulfilled. Returns a new + * Promise that will be fulfilled when the callback is complete. It allows + * chaining. If the callback itself returns a Promise, then the one returned + * by "then" will not be fulfilled until that one returned by the callback + * is fulfilled. + * @param {Function} resolvedCallback Function that is called when this + * Promise is resolved. Once the callback is complete, then the Promise + * returned by "then" will also be fulfilled. + * @param {Function} rejectedCallback Function that is called when this + * Promise is rejected with an error. Once the callback is complete, then + * the promise returned by "then" with be resolved successfully. If + * rejectedCallback is null, or it returns a rejected Promise, then the + * Promise returned by "then" will be rejected with that error. + * @return {Parse.Promise} A new Promise that will be fulfilled after this + * Promise is fulfilled and either callback has completed. If the callback + * returned a Promise, then this Promise will not be fulfilled until that + * one is. + */ + then: function(resolvedCallback, rejectedCallback) { + var promise = new Parse.Promise(); + + var wrappedResolvedCallback = function() { + var result = arguments; + if (resolvedCallback) { + if (Parse.Promise._isPromisesAPlusCompliant) { + try { + result = [resolvedCallback.apply(this, result)]; + } catch (e) { + result = [Parse.Promise.error(e)]; + } + } else { + result = [resolvedCallback.apply(this, result)]; + } + } + if (result.length === 1 && Parse.Promise.is(result[0])) { + result[0].then(function() { + promise.resolve.apply(promise, arguments); + }, function(error) { + promise.reject(error); + }); + } else { + promise.resolve.apply(promise, result); + } + }; + + var wrappedRejectedCallback = function(error) { + var result = []; + if (rejectedCallback) { + if (Parse.Promise._isPromisesAPlusCompliant) { + try { + result = [rejectedCallback(error)]; + } catch (e) { + result = [Parse.Promise.error(e)]; + } + } else { + result = [rejectedCallback(error)]; + } + if (result.length === 1 && Parse.Promise.is(result[0])) { + result[0].then(function() { + promise.resolve.apply(promise, arguments); + }, function(error) { + promise.reject(error); + }); + } else { + if (Parse.Promise._isPromisesAPlusCompliant) { + promise.resolve.apply(promise, result); + } else { + promise.reject(result[0]); + } + } + } else { + promise.reject(error); + } + }; + + var runLater = function(func) { + func.call(); + }; + if (Parse.Promise._isPromisesAPlusCompliant) { + if (typeof(window) !== 'undefined' && window.setTimeout) { + runLater = function(func) { + window.setTimeout(func, 0); + }; + } else if (typeof(process) !== 'undefined' && process.nextTick) { + runLater = function(func) { + process.nextTick(func); + }; + } + } + + var self = this; + if (this._resolved) { + runLater(function() { + wrappedResolvedCallback.apply(self, self._result); + }); + } else if (this._rejected) { + runLater(function() { + wrappedRejectedCallback(self._error); + }); + } else { + this._resolvedCallbacks.push(wrappedResolvedCallback); + this._rejectedCallbacks.push(wrappedRejectedCallback); + } + + return promise; + }, + + /** + * Add handlers to be called when the promise + * is either resolved or rejected + */ + always: function(callback) { + return this.then(callback, callback); + }, + + /** + * Add handlers to be called when the Promise object is resolved + */ + done: function(callback) { + return this.then(callback); + }, + + /** + * Add handlers to be called when the Promise object is rejected + */ + fail: function(callback) { + return this.then(null, callback); + }, + + /** + * Run the given callbacks after this promise is fulfilled. + * @param optionsOrCallback {} A Backbone-style options callback, or a + * callback function. If this is an options object and contains a "model" + * attributes, that will be passed to error callbacks as the first argument. + * @param model {} If truthy, this will be passed as the first result of + * error callbacks. This is for Backbone-compatability. + * @return {Parse.Promise} A promise that will be resolved after the + * callbacks are run, with the same result as this. + */ + _thenRunCallbacks: function(optionsOrCallback, model) { + var options; + if (_.isFunction(optionsOrCallback)) { + var callback = optionsOrCallback; + options = { + success: function(result) { + callback(result, null); + }, + error: function(error) { + callback(null, error); + } + }; + } else { + options = _.clone(optionsOrCallback); + } + options = options || {}; + + return this.then(function(result) { + if (options.success) { + options.success.apply(this, arguments); + } else if (model) { + // When there's no callback, a sync event should be triggered. + model.trigger('sync', model, result, options); + } + return Parse.Promise.as.apply(Parse.Promise, arguments); + }, function(error) { + if (options.error) { + if (!_.isUndefined(model)) { + options.error(model, error); + } else { + options.error(error); + } + } else if (model) { + // When there's no error callback, an error event should be triggered. + model.trigger('error', model, error, options); + } + // By explicitly returning a rejected Promise, this will work with + // either jQuery or Promises/A semantics. + return Parse.Promise.error(error); + }); + }, + + /** + * Adds a callback function that should be called regardless of whether + * this promise failed or succeeded. The callback will be given either the + * array of results for its first argument, or the error as its second, + * depending on whether this Promise was rejected or resolved. Returns a + * new Promise, like "then" would. + * @param {Function} continuation the callback. + */ + _continueWith: function(continuation) { + return this.then(function() { + return continuation(arguments, null); + }, function(error) { + return continuation(null, error); + }); + } + + }); + +}(this)); + +/*jshint bitwise:false *//*global FileReader: true, File: true */ +(function(root) { + root.Parse = root.Parse || {}; + var Parse = root.Parse; + var _ = Parse._; + + var b64Digit = function(number) { + if (number < 26) { + return String.fromCharCode(65 + number); + } + if (number < 52) { + return String.fromCharCode(97 + (number - 26)); + } + if (number < 62) { + return String.fromCharCode(48 + (number - 52)); + } + if (number === 62) { + return "+"; + } + if (number === 63) { + return "/"; + } + throw "Tried to encode large digit " + number + " in base64."; + }; + + var encodeBase64 = function(array) { + var chunks = []; + chunks.length = Math.ceil(array.length / 3); + _.times(chunks.length, function(i) { + var b1 = array[i * 3]; + var b2 = array[i * 3 + 1] || 0; + var b3 = array[i * 3 + 2] || 0; + + var has2 = (i * 3 + 1) < array.length; + var has3 = (i * 3 + 2) < array.length; + + chunks[i] = [ + b64Digit((b1 >> 2) & 0x3F), + b64Digit(((b1 << 4) & 0x30) | ((b2 >> 4) & 0x0F)), + has2 ? b64Digit(((b2 << 2) & 0x3C) | ((b3 >> 6) & 0x03)) : "=", + has3 ? b64Digit(b3 & 0x3F) : "=" + ].join(""); + }); + return chunks.join(""); + }; + + /** + * Reads a File using a FileReader. + * @param file {File} the File to read. + * @param type {String} (optional) the mimetype to override with. + * @return {Parse.Promise} A Promise that will be fulfilled with a + * base64-encoded string of the data and its mime type. + */ + var readAsync = function(file, type) { + var promise = new Parse.Promise(); + + if (typeof(FileReader) === "undefined") { + return Parse.Promise.error(new Parse.Error( + Parse.Error.FILE_READ_ERROR, + "Attempted to use a FileReader on an unsupported browser.")); + } + + var reader = new FileReader(); + reader.onloadend = function() { + if (reader.readyState !== 2) { + promise.reject(new Parse.Error( + Parse.Error.FILE_READ_ERROR, + "Error reading file.")); + return; + } + + var dataURL = reader.result; + var matches = /^data:([^;]*);base64,(.*)$/.exec(dataURL); + if (!matches) { + promise.reject(new Parse.Error( + Parse.Error.FILE_READ_ERROR, + "Unable to interpret data URL: " + dataURL)); + return; + } + + promise.resolve(matches[2], type || matches[1]); + }; + reader.readAsDataURL(file); + return promise; + }; + + /** + * A Parse.File is a local representation of a file that is saved to the Parse + * cloud. + * @class + * @param name {String} The file's name. This will be prefixed by a unique + * value once the file has finished saving. The file name must begin with + * an alphanumeric character, and consist of alphanumeric characters, + * periods, spaces, underscores, or dashes. + * @param data {Array} The data for the file, as either: + * 1. an Array of byte value Numbers, or + * 2. an Object like { base64: "..." } with a base64-encoded String. + * 3. a File object selected with a file upload control. (3) only works + * in Firefox 3.6+, Safari 6.0.2+, Chrome 7+, and IE 10+. + * For example:
+   * var fileUploadControl = $("#profilePhotoFileUpload")[0];
+   * if (fileUploadControl.files.length > 0) {
+   *   var file = fileUploadControl.files[0];
+   *   var name = "photo.jpg";
+   *   var parseFile = new Parse.File(name, file);
+   *   parseFile.save().then(function() {
+   *     // The file has been saved to Parse.
+   *   }, function(error) {
+   *     // The file either could not be read, or could not be saved to Parse.
+   *   });
+   * }
+ * @param type {String} Optional Content-Type header to use for the file. If + * this is omitted, the content type will be inferred from the name's + * extension. + */ + Parse.File = function(name, data, type) { + this._name = name; + + // Guess the content type from the extension if we need to. + var extension = /\.([^.]*)$/.exec(name); + if (extension) { + extension = extension[1].toLowerCase(); + } + var specifiedType = type || ''; + + if (_.isArray(data)) { + this._source = Parse.Promise.as(encodeBase64(data), specifiedType); + } else if (data && data.base64) { + // if it contains data uri, extract based64 and the type out of it. + /*jslint maxlen: 1000*/ + var dataUriRegexp = /^data:([a-zA-Z]*\/[a-zA-Z+.-]*);(charset=[a-zA-Z0-9\-\/\s]*,)?base64,(\S+)/; + /*jslint maxlen: 80*/ + + var matches = dataUriRegexp.exec(data.base64); + if (matches && matches.length > 0) { + // if data URI with charset, there will have 4 matches. + this._source = Parse.Promise.as( + (matches.length === 4 ? matches[3] : matches[2]), matches[1] + ); + } else { + this._source = Parse.Promise.as(data.base64, specifiedType); + } + } else if (typeof(File) !== "undefined" && data instanceof File) { + this._source = readAsync(data, type); + } else if (_.isString(data)) { + throw "Creating a Parse.File from a String is not yet supported."; + } + }; + + Parse.File.prototype = { + + /** + * Gets the name of the file. Before save is called, this is the filename + * given by the user. After save is called, that name gets prefixed with a + * unique identifier. + */ + name: function() { + return this._name; + }, + + /** + * Gets the url of the file. It is only available after you save the file or + * after you get the file from a Parse.Object. + * @return {String} + */ + url: function() { + return this._url; + }, + + /** + * Saves the file to the Parse cloud. + * @param {Object} options A Backbone-style options object. + * @return {Parse.Promise} Promise that is resolved when the save finishes. + */ + save: function(options) { + options= options || {}; + + var self = this; + if (!self._previousSave) { + self._previousSave = self._source.then(function(base64, type) { + var data = { + base64: base64, + _ContentType: type + }; + return Parse._request({ + route: "files", + className: self._name, + method: 'POST', + data: data, + useMasterKey: options.useMasterKey + }); + + }).then(function(response) { + self._name = response.name; + self._url = response.url; + return self; + }); + } + return self._previousSave._thenRunCallbacks(options); + } + }; + +}(this)); + +// Parse.Object is analogous to the Java ParseObject. +// It also implements the same interface as a Backbone model. + +(function(root) { + root.Parse = root.Parse || {}; + var Parse = root.Parse; + var _ = Parse._; + + /** + * Creates a new model with defined attributes. A client id (cid) is + * automatically generated and assigned for you. + * + *

You won't normally call this method directly. It is recommended that + * you use a subclass of Parse.Object instead, created by calling + * extend.

+ * + *

However, if you don't want to use a subclass, or aren't sure which + * subclass is appropriate, you can use this form:

+   *     var object = new Parse.Object("ClassName");
+   * 
+ * That is basically equivalent to:
+   *     var MyClass = Parse.Object.extend("ClassName");
+   *     var object = new MyClass();
+   * 

+ * + * @param {Object} attributes The initial set of data to store in the object. + * @param {Object} options A set of Backbone-like options for creating the + * object. The only option currently supported is "collection". + * @see Parse.Object.extend + * + * @class + * + *

The fundamental unit of Parse data, which implements the Backbone Model + * interface.

+ */ + Parse.Object = function(attributes, options) { + // Allow new Parse.Object("ClassName") as a shortcut to _create. + if (_.isString(attributes)) { + return Parse.Object._create.apply(this, arguments); + } + + attributes = attributes || {}; + if (options && options.parse) { + attributes = this.parse(attributes); + } + var defaults = Parse._getValue(this, 'defaults'); + if (defaults) { + attributes = _.extend({}, defaults, attributes); + } + if (options && options.collection) { + this.collection = options.collection; + } + + this._serverData = {}; // The last known data for this object from cloud. + this._opSetQueue = [{}]; // List of sets of changes to the data. + this.attributes = {}; // The best estimate of this's current data. + + this._hashedJSON = {}; // Hash of values of containers at last save. + this._escapedAttributes = {}; + this.cid = _.uniqueId('c'); + this.changed = {}; + this._silent = {}; + this._pending = {}; + if (!this.set(attributes, {silent: true})) { + throw new Error("Can't create an invalid Parse.Object"); + } + this.changed = {}; + this._silent = {}; + this._pending = {}; + this._hasData = true; + this._previousAttributes = _.clone(this.attributes); + this.initialize.apply(this, arguments); + }; + + /** + * The ID of this object, unique within its class. + * @name id + * @type String + * @field + * @memberOf Parse.Object.prototype + */ + + /** + * The first time this object was saved on the server. + * @name createdAt + * @type Date + * @field + * @memberOf Parse.Object.prototype + */ + + /** + * The last time this object was updated on the server. + * @name updatedAt + * @type Date + * @field + * @memberOf Parse.Object.prototype + */ + + /** + * Saves the given list of Parse.Object. + * If any error is encountered, stops and calls the error handler. + * + *
+   *   Parse.Object.saveAll([object1, object2, ...], {
+   *     success: function(list) {
+   *       // All the objects were saved.
+   *     },
+   *     error: function(error) {
+   *       // An error occurred while saving one of the objects.
+   *     },
+   *   });
+   * 
+ * + * @param {Array} list A list of Parse.Object. + * @param {Object} options A Backbone-style callback object. + * Valid options are:
    + *
  • useMasterKey: In Cloud Code and Node only, causes the Master Key to + * be used for this request. + *
  • sessionToken: A valid session token, used for making a request on + * behalf of a specific user. + *
+ */ + Parse.Object.saveAll = function(list, options) { + options = options || {}; + return Parse.Object._deepSaveAsync(list, { + useMasterKey: options.useMasterKey, + sessionToken: options.sessionToken + })._thenRunCallbacks(options); + }; + + /** + * Destroy the given list of models on the server if it was already persisted. + * Optimistically removes each model from its collection, if it has one. + * If `wait: true` is passed, waits for the server to respond before removal. + * + *

Unlike saveAll, if an error occurs while deleting an individual model, + * this method will continue trying to delete the rest of the models if + * possible, except in the case of a fatal error like a connection error. + * + *

In particular, the Parse.Error object returned in the case of error may + * be one of two types: + * + *

    + *
  • A Parse.Error.AGGREGATE_ERROR. This object's "errors" property is an + * array of other Parse.Error objects. Each error object in this array + * has an "object" property that references the object that could not be + * deleted (for instance, because that object could not be found).
  • + *
  • A non-aggregate Parse.Error. This indicates a serious error that + * caused the delete operation to be aborted partway through (for + * instance, a connection failure in the middle of the delete).
  • + *
+ * + *
+   *   Parse.Object.destroyAll([object1, object2, ...], {
+   *     success: function() {
+   *       // All the objects were deleted.
+   *     },
+   *     error: function(error) {
+   *       // An error occurred while deleting one or more of the objects.
+   *       // If this is an aggregate error, then we can inspect each error
+   *       // object individually to determine the reason why a particular
+   *       // object was not deleted.
+   *       if (error.code == Parse.Error.AGGREGATE_ERROR) {
+   *         for (var i = 0; i < error.errors.length; i++) {
+   *           console.log("Couldn't delete " + error.errors[i].object.id +
+   *             "due to " + error.errors[i].message);
+   *         }
+   *       } else {
+   *         console.log("Delete aborted because of " + error.message);
+   *       }
+   *     },
+   *   });
+   * 
+ * + * @param {Array} list A list of Parse.Object. + * @param {Object} options A Backbone-style callback object. + * Valid options are:
    + *
  • useMasterKey: In Cloud Code and Node only, causes the Master Key to + * be used for this request. + *
  • sessionToken: A valid session token, used for making a request on + * behalf of a specific user. + *
+ * @return {Parse.Promise} A promise that is fulfilled when the destroyAll + * completes. + */ + Parse.Object.destroyAll = function(list, options) { + options = options || {}; + + var triggerDestroy = function(object) { + object.trigger('destroy', object, object.collection, options); + }; + + var errors = []; + var destroyBatch = function(batch) { + var promise = Parse.Promise.as(); + + if (batch.length > 0) { + promise = promise.then(function() { + return Parse._request({ + route: "batch", + method: "POST", + useMasterKey: options.useMasterKey, + sessionToken: options.sessionToken, + data: { + requests: _.map(batch, function(object) { + return { + method: "DELETE", + path: "/1/classes/" + object.className + "/" + object.id + }; + }) + } + }); + }).then(function(responses, status, xhr) { + Parse._arrayEach(batch, function(object, i) { + if (responses[i].success && options.wait) { + triggerDestroy(object); + } else if (responses[i].error) { + var error = new Parse.Error(responses[i].error.code, + responses[i].error.error); + error.object = object; + + errors.push(error); + } + }); + }); + } + + return promise; + }; + + var promise = Parse.Promise.as(); + var batch = []; + Parse._arrayEach(list, function(object, i) { + if (!object.id || !options.wait) { + triggerDestroy(object); + } + + if (object.id) { + batch.push(object); + } + + if (batch.length === 20 || i+1 === list.length) { + var thisBatch = batch; + batch = []; + + promise = promise.then(function() { + return destroyBatch(thisBatch); + }); + } + }); + + return promise.then(function() { + if (errors.length === 0) { + return true; + } else { + var error = new Parse.Error(Parse.Error.AGGREGATE_ERROR, + "Error deleting an object in destroyAll"); + error.errors = errors; + + return Parse.Promise.error(error); + } + })._thenRunCallbacks(options); + }; + + /** + * Fetches the given list of Parse.Object. + * If any error is encountered, stops and calls the error handler. + * + *
+   *   Parse.Object.fetchAll([object1, object2, ...], {
+   *     success: function(list) {
+   *       // All the objects were fetched.
+   *     },
+   *     error: function(error) {
+   *       // An error occurred while fetching one of the objects.
+   *     },
+   *   });
+   * 
+ * + * @param {Array} list A list of Parse.Object. + * @param {Object} options A Backbone-style callback object. + * Valid options are:
    + *
  • success: A Backbone-style success callback. + *
  • error: An Backbone-style error callback. + *
+ */ + Parse.Object.fetchAll = function(list, options) { + return Parse.Object._fetchAll( + list, + true + )._thenRunCallbacks(options); + }; + + /** + * Fetches the given list of Parse.Object if needed. + * If any error is encountered, stops and calls the error handler. + * + *
+   *   Parse.Object.fetchAllIfNeeded([object1, ...], {
+   *     success: function(list) {
+   *       // Objects were fetched and updated.
+   *     },
+   *     error: function(error) {
+   *       // An error occurred while fetching one of the objects.
+   *     },
+   *   });
+   * 
+ * + * @param {Array} list A list of Parse.Object. + * @param {Object} options A Backbone-style callback object. + * Valid options are:
    + *
  • success: A Backbone-style success callback. + *
  • error: An Backbone-style error callback. + *
+ */ + Parse.Object.fetchAllIfNeeded = function(list, options) { + return Parse.Object._fetchAll( + list, + false + )._thenRunCallbacks(options); + }; + + // Attach all inheritable methods to the Parse.Object prototype. + _.extend(Parse.Object.prototype, Parse.Events, + /** @lends Parse.Object.prototype */ { + _existed: false, + + /** + * Initialize is an empty function by default. Override it with your own + * initialization logic. + */ + initialize: function(){}, + + /** + * Returns a JSON version of the object suitable for saving to Parse. + * @return {Object} + */ + toJSON: function() { + var json = this._toFullJSON(); + Parse._arrayEach(["__type", "className"], + function(key) { delete json[key]; }); + return json; + }, + + _toFullJSON: function(seenObjects) { + var json = _.clone(this.attributes); + Parse._objectEach(json, function(val, key) { + json[key] = Parse._encode(val, seenObjects); + }); + Parse._objectEach(this._operations, function(val, key) { + json[key] = val; + }); + + if (_.has(this, "id")) { + json.objectId = this.id; + } + if (_.has(this, "createdAt")) { + if (_.isDate(this.createdAt)) { + json.createdAt = this.createdAt.toJSON(); + } else { + json.createdAt = this.createdAt; + } + } + + if (_.has(this, "updatedAt")) { + if (_.isDate(this.updatedAt)) { + json.updatedAt = this.updatedAt.toJSON(); + } else { + json.updatedAt = this.updatedAt; + } + } + json.__type = "Object"; + json.className = this.className; + return json; + }, + + /** + * Updates _hashedJSON to reflect the current state of this object. + * Adds any changed hash values to the set of pending changes. + */ + _refreshCache: function() { + var self = this; + if (self._refreshingCache) { + return; + } + self._refreshingCache = true; + Parse._objectEach(this.attributes, function(value, key) { + if (value instanceof Parse.Object) { + value._refreshCache(); + } else if (_.isObject(value)) { + var objectArray = false; + if (_.isArray(value)) { + // We don't cache arrays of Parse.Objects + _.each(value, function(arrVal) { + if (arrVal instanceof Parse.Object) { + objectArray = true; + arrVal._refreshCache(); + } + }); + } + if (!objectArray && self._resetCacheForKey(key)) { + self.set(key, new Parse.Op.Set(value), { silent: true }); + } + } + }); + delete self._refreshingCache; + }, + + /** + * Returns true if this object has been modified since its last + * save/refresh. If an attribute is specified, it returns true only if that + * particular attribute has been modified since the last save/refresh. + * @param {String} attr An attribute name (optional). + * @return {Boolean} + */ + dirty: function(attr) { + this._refreshCache(); + + var currentChanges = _.last(this._opSetQueue); + + if (attr) { + return (currentChanges[attr] ? true : false); + } + if (!this.id) { + return true; + } + if (_.keys(currentChanges).length > 0) { + return true; + } + return false; + }, + + /** + * Returns an array of keys that have been modified since last save/refresh + * @return {Array of string} + */ + dirtyKeys: function() { + return _.keys(_.last(this._opSetQueue)); + }, + + /** + * Gets a Pointer referencing this Object. + */ + _toPointer: function() { + if (!this.id) { + throw new Error("Can't serialize an unsaved Parse.Object"); + } + return { __type: "Pointer", + className: this.className, + objectId: this.id }; + }, + + /** + * Gets the value of an attribute. + * @param {String} attr The string name of an attribute. + */ + get: function(attr) { + return this.attributes[attr]; + }, + + /** + * Gets a relation on the given class for the attribute. + * @param String attr The attribute to get the relation for. + */ + relation: function(attr) { + var value = this.get(attr); + if (value) { + if (!(value instanceof Parse.Relation)) { + throw "Called relation() on non-relation field " + attr; + } + value._ensureParentAndKey(this, attr); + return value; + } else { + return new Parse.Relation(this, attr); + } + }, + + /** + * Gets the HTML-escaped value of an attribute. + */ + escape: function(attr) { + var html = this._escapedAttributes[attr]; + if (html) { + return html; + } + var val = this.attributes[attr]; + var escaped; + if (Parse._isNullOrUndefined(val)) { + escaped = ''; + } else { + escaped = _.escape(val.toString()); + } + this._escapedAttributes[attr] = escaped; + return escaped; + }, + + /** + * Returns true if the attribute contains a value that is not + * null or undefined. + * @param {String} attr The string name of the attribute. + * @return {Boolean} + */ + has: function(attr) { + return !Parse._isNullOrUndefined(this.attributes[attr]); + }, + + /** + * Pulls "special" fields like objectId, createdAt, etc. out of attrs + * and puts them on "this" directly. Removes them from attrs. + * @param attrs - A dictionary with the data for this Parse.Object. + */ + _mergeMagicFields: function(attrs) { + // Check for changes of magic fields. + var model = this; + var specialFields = ["id", "objectId", "createdAt", "updatedAt"]; + Parse._arrayEach(specialFields, function(attr) { + if (attrs[attr]) { + if (attr === "objectId") { + model.id = attrs[attr]; + } else if ((attr === "createdAt" || attr === "updatedAt") && + !_.isDate(attrs[attr])) { + model[attr] = Parse._parseDate(attrs[attr]); + } else { + model[attr] = attrs[attr]; + } + delete attrs[attr]; + } + }); + }, + + /** + * Copies the given serverData to "this", refreshes attributes, and + * clears pending changes; + */ + _copyServerData: function(serverData) { + // Copy server data + var tempServerData = {}; + Parse._objectEach(serverData, function(value, key) { + tempServerData[key] = Parse._decode(key, value); + }); + this._serverData = tempServerData; + + // Refresh the attributes. + this._rebuildAllEstimatedData(); + + + // Clear out any changes the user might have made previously. + this._refreshCache(); + this._opSetQueue = [{}]; + + // Refresh the attributes again. + this._rebuildAllEstimatedData(); + }, + + /** + * Merges another object's attributes into this object. + */ + _mergeFromObject: function(other) { + if (!other) { + return; + } + + // This does the inverse of _mergeMagicFields. + this.id = other.id; + this.createdAt = other.createdAt; + this.updatedAt = other.updatedAt; + + this._copyServerData(other._serverData); + + this._hasData = true; + }, + + /** + * Returns the json to be sent to the server. + */ + _startSave: function() { + this._opSetQueue.push({}); + }, + + /** + * Called when a save fails because of an error. Any changes that were part + * of the save need to be merged with changes made after the save. This + * might throw an exception is you do conflicting operations. For example, + * if you do: + * object.set("foo", "bar"); + * object.set("invalid field name", "baz"); + * object.save(); + * object.increment("foo"); + * then this will throw when the save fails and the client tries to merge + * "bar" with the +1. + */ + _cancelSave: function() { + var self = this; + var failedChanges = _.first(this._opSetQueue); + this._opSetQueue = _.rest(this._opSetQueue); + var nextChanges = _.first(this._opSetQueue); + Parse._objectEach(failedChanges, function(op, key) { + var op1 = failedChanges[key]; + var op2 = nextChanges[key]; + if (op1 && op2) { + nextChanges[key] = op2._mergeWithPrevious(op1); + } else if (op1) { + nextChanges[key] = op1; + } + }); + this._saving = this._saving - 1; + }, + + /** + * Called when a save completes successfully. This merges the changes that + * were saved into the known server data, and overrides it with any data + * sent directly from the server. + */ + _finishSave: function(serverData) { + // Grab a copy of any object referenced by this object. These instances + // may have already been fetched, and we don't want to lose their data. + // Note that doing it like this means we will unify separate copies of the + // same object, but that's a risk we have to take. + var fetchedObjects = {}; + Parse._traverse(this.attributes, function(object) { + if (object instanceof Parse.Object && object.id && object._hasData) { + fetchedObjects[object.id] = object; + } + }); + + var savedChanges = _.first(this._opSetQueue); + this._opSetQueue = _.rest(this._opSetQueue); + this._applyOpSet(savedChanges, this._serverData); + this._mergeMagicFields(serverData); + var self = this; + Parse._objectEach(serverData, function(value, key) { + self._serverData[key] = Parse._decode(key, value); + + // Look for any objects that might have become unfetched and fix them + // by replacing their values with the previously observed values. + var fetched = Parse._traverse(self._serverData[key], function(object) { + if (object instanceof Parse.Object && fetchedObjects[object.id]) { + return fetchedObjects[object.id]; + } + }); + if (fetched) { + self._serverData[key] = fetched; + } + }); + this._rebuildAllEstimatedData(); + this._saving = this._saving - 1; + }, + + /** + * Called when a fetch or login is complete to set the known server data to + * the given object. + */ + _finishFetch: function(serverData, hasData) { + + this._opSetQueue = [{}]; + + // Bring in all the new server data. + this._mergeMagicFields(serverData); + this._copyServerData(serverData); + + this._hasData = hasData; + }, + + /** + * Applies the set of Parse.Op in opSet to the object target. + */ + _applyOpSet: function(opSet, target) { + var self = this; + Parse._objectEach(opSet, function(change, key) { + target[key] = change._estimate(target[key], self, key); + if (target[key] === Parse.Op._UNSET) { + delete target[key]; + } + }); + }, + + /** + * Replaces the cached value for key with the current value. + * Returns true if the new value is different than the old value. + */ + _resetCacheForKey: function(key) { + var value = this.attributes[key]; + if (_.isObject(value) && + !(value instanceof Parse.Object) && + !(value instanceof Parse.File)) { + value = value.toJSON ? value.toJSON() : value; + var json = JSON.stringify(value); + if (this._hashedJSON[key] !== json) { + var wasSet = !!this._hashedJSON[key]; + this._hashedJSON[key] = json; + return wasSet; + } + } + return false; + }, + + /** + * Populates attributes[key] by starting with the last known data from the + * server, and applying all of the local changes that have been made to that + * key since then. + */ + _rebuildEstimatedDataForKey: function(key) { + var self = this; + delete this.attributes[key]; + if (this._serverData[key]) { + this.attributes[key] = this._serverData[key]; + } + Parse._arrayEach(this._opSetQueue, function(opSet) { + var op = opSet[key]; + if (op) { + self.attributes[key] = op._estimate(self.attributes[key], self, key); + if (self.attributes[key] === Parse.Op._UNSET) { + delete self.attributes[key]; + } else { + self._resetCacheForKey(key); + } + } + }); + }, + + /** + * Populates attributes by starting with the last known data from the + * server, and applying all of the local changes that have been made since + * then. + */ + _rebuildAllEstimatedData: function() { + var self = this; + + var previousAttributes = _.clone(this.attributes); + + this.attributes = _.clone(this._serverData); + Parse._arrayEach(this._opSetQueue, function(opSet) { + self._applyOpSet(opSet, self.attributes); + Parse._objectEach(opSet, function(op, key) { + self._resetCacheForKey(key); + }); + }); + + // Trigger change events for anything that changed because of the fetch. + Parse._objectEach(previousAttributes, function(oldValue, key) { + if (self.attributes[key] !== oldValue) { + self.trigger('change:' + key, self, self.attributes[key], {}); + } + }); + Parse._objectEach(this.attributes, function(newValue, key) { + if (!_.has(previousAttributes, key)) { + self.trigger('change:' + key, self, newValue, {}); + } + }); + }, + + /** + * Sets a hash of model attributes on the object, firing + * "change" unless you choose to silence it. + * + *

You can call it with an object containing keys and values, or with one + * key and value. For example:

+     *   gameTurn.set({
+     *     player: player1,
+     *     diceRoll: 2
+     *   }, {
+     *     error: function(gameTurnAgain, error) {
+     *       // The set failed validation.
+     *     }
+     *   });
+     *
+     *   game.set("currentPlayer", player2, {
+     *     error: function(gameTurnAgain, error) {
+     *       // The set failed validation.
+     *     }
+     *   });
+     *
+     *   game.set("finished", true);

+ * + * @param {String} key The key to set. + * @param {} value The value to give it. + * @param {Object} options A set of Backbone-like options for the set. + * The only supported options are silent, + * error, and promise. + * @return {Boolean} true if the set succeeded. + * @see Parse.Object#validate + * @see Parse.Error + */ + set: function(key, value, options) { + var attrs, attr; + if (_.isObject(key) || Parse._isNullOrUndefined(key)) { + attrs = key; + Parse._objectEach(attrs, function(v, k) { + attrs[k] = Parse._decode(k, v); + }); + options = value; + } else { + attrs = {}; + attrs[key] = Parse._decode(key, value); + } + + // Extract attributes and options. + options = options || {}; + if (!attrs) { + return this; + } + if (attrs instanceof Parse.Object) { + attrs = attrs.attributes; + } + + var self = this; + Parse._objectEach(attrs, function(unused_value, key) { + if (self.constructor.readOnlyAttributes && + self.constructor.readOnlyAttributes[key]) { + throw new Error('Cannot modify readonly key: ' + key); + } + }); + + // If the unset option is used, every attribute should be a Unset. + if (options.unset) { + Parse._objectEach(attrs, function(unused_value, key) { + attrs[key] = new Parse.Op.Unset(); + }); + } + + // Apply all the attributes to get the estimated values. + var dataToValidate = _.clone(attrs); + Parse._objectEach(dataToValidate, function(value, key) { + if (value instanceof Parse.Op) { + dataToValidate[key] = value._estimate(self.attributes[key], + self, key); + if (dataToValidate[key] === Parse.Op._UNSET) { + delete dataToValidate[key]; + } + } + }); + + // Run validation. + if (!this._validate(attrs, options)) { + return false; + } + + this._mergeMagicFields(attrs); + + options.changes = {}; + var escaped = this._escapedAttributes; + var prev = this._previousAttributes || {}; + + // Update attributes. + Parse._arrayEach(_.keys(attrs), function(attr) { + var val = attrs[attr]; + + // If this is a relation object we need to set the parent correctly, + // since the location where it was parsed does not have access to + // this object. + if (val instanceof Parse.Relation) { + val.parent = self; + } + + if (!(val instanceof Parse.Op)) { + val = new Parse.Op.Set(val); + } + + // See if this change will actually have any effect. + var isRealChange = true; + if (val instanceof Parse.Op.Set && + _.isEqual(self.attributes[attr], val.value)) { + isRealChange = false; + } + + if (isRealChange) { + delete escaped[attr]; + if (options.silent) { + self._silent[attr] = true; + } else { + options.changes[attr] = true; + } + } + + var currentChanges = _.last(self._opSetQueue); + currentChanges[attr] = val._mergeWithPrevious(currentChanges[attr]); + self._rebuildEstimatedDataForKey(attr); + + if (isRealChange) { + self.changed[attr] = self.attributes[attr]; + if (!options.silent) { + self._pending[attr] = true; + } + } else { + delete self.changed[attr]; + delete self._pending[attr]; + } + }); + + if (!options.silent) { + this.change(options); + } + return this; + }, + + /** + * Remove an attribute from the model, firing "change" unless + * you choose to silence it. This is a noop if the attribute doesn't + * exist. + */ + unset: function(attr, options) { + options = options || {}; + options.unset = true; + return this.set(attr, null, options); + }, + + /** + * Atomically increments the value of the given attribute the next time the + * object is saved. If no amount is specified, 1 is used by default. + * + * @param attr {String} The key. + * @param amount {Number} The amount to increment by. + */ + increment: function(attr, amount) { + if (_.isUndefined(amount) || _.isNull(amount)) { + amount = 1; + } + return this.set(attr, new Parse.Op.Increment(amount)); + }, + + /** + * Atomically add an object to the end of the array associated with a given + * key. + * @param attr {String} The key. + * @param item {} The item to add. + */ + add: function(attr, item) { + return this.set(attr, new Parse.Op.Add([item])); + }, + + /** + * Atomically add an object to the array associated with a given key, only + * if it is not already present in the array. The position of the insert is + * not guaranteed. + * + * @param attr {String} The key. + * @param item {} The object to add. + */ + addUnique: function(attr, item) { + return this.set(attr, new Parse.Op.AddUnique([item])); + }, + + /** + * Atomically remove all instances of an object from the array associated + * with a given key. + * + * @param attr {String} The key. + * @param item {} The object to remove. + */ + remove: function(attr, item) { + return this.set(attr, new Parse.Op.Remove([item])); + }, + + /** + * Returns an instance of a subclass of Parse.Op describing what kind of + * modification has been performed on this field since the last time it was + * saved. For example, after calling object.increment("x"), calling + * object.op("x") would return an instance of Parse.Op.Increment. + * + * @param attr {String} The key. + * @returns {Parse.Op} The operation, or undefined if none. + */ + op: function(attr) { + return _.last(this._opSetQueue)[attr]; + }, + + /** + * Clear all attributes on the model, firing "change" unless + * you choose to silence it. + */ + clear: function(options) { + options = options || {}; + options.unset = true; + var keysToClear = _.extend(this.attributes, this._operations); + return this.set(keysToClear, options); + }, + + /** + * Returns a JSON-encoded set of operations to be sent with the next save + * request. + */ + _getSaveJSON: function() { + var json = _.clone(_.first(this._opSetQueue)); + Parse._objectEach(json, function(op, key) { + json[key] = op.toJSON(); + }); + return json; + }, + + /** + * Returns true if this object can be serialized for saving. + */ + _canBeSerialized: function() { + return Parse.Object._canBeSerializedAsValue(this.attributes); + }, + + /** + * Fetch the model from the server. If the server's representation of the + * model differs from its current attributes, they will be overriden, + * triggering a "change" event. + * + * @param {Object} options A Backbone-style callback object. + * Valid options are:
    + *
  • success: A Backbone-style success callback. + *
  • error: An Backbone-style error callback. + *
  • useMasterKey: In Cloud Code and Node only, causes the Master Key to + * be used for this request. + *
  • sessionToken: A valid session token, used for making a request on + * behalf of a specific user. + *
+ * @return {Parse.Promise} A promise that is fulfilled when the fetch + * completes. + */ + fetch: function(options) { + var self = this; + options = options || {}; + var request = Parse._request({ + method: 'GET', + route: "classes", + className: this.className, + objectId: this.id, + useMasterKey: options.useMasterKey, + sessionToken: options.sessionToken + }); + return request.then(function(response, status, xhr) { + self._finishFetch(self.parse(response, status, xhr), true); + return self; + })._thenRunCallbacks(options, this); + }, + + /** + * Set a hash of model attributes, and save the model to the server. + * updatedAt will be updated when the request returns. + * You can either call it as:
+     *   object.save();
+ * or
+     *   object.save(null, options);
+ * or
+     *   object.save(attrs, options);
+ * or
+     *   object.save(key, value, options);
+ * + * For example,
+     *   gameTurn.save({
+     *     player: "Jake Cutter",
+     *     diceRoll: 2
+     *   }, {
+     *     success: function(gameTurnAgain) {
+     *       // The save was successful.
+     *     },
+     *     error: function(gameTurnAgain, error) {
+     *       // The save failed.  Error is an instance of Parse.Error.
+     *     }
+     *   });
+ * or with promises:
+     *   gameTurn.save({
+     *     player: "Jake Cutter",
+     *     diceRoll: 2
+     *   }).then(function(gameTurnAgain) {
+     *     // The save was successful.
+     *   }, function(error) {
+     *     // The save failed.  Error is an instance of Parse.Error.
+     *   });
+ * + * @param {Object} options A Backbone-style callback object. + * Valid options are:
    + *
  • wait: Set to true to wait for the server to confirm a successful + * save before modifying the attributes on the object. + *
  • silent: Set to true to avoid firing the `set` event. + *
  • success: A Backbone-style success callback. + *
  • error: An Backbone-style error callback. + *
  • useMasterKey: In Cloud Code and Node only, causes the Master Key to + * be used for this request. + *
  • sessionToken: A valid session token, used for making a request on + * behalf of a specific user. + *
+ * @return {Parse.Promise} A promise that is fulfilled when the save + * completes. + * @see Parse.Error + */ + save: function(arg1, arg2, arg3) { + var i, attrs, current, options, saved; + if (_.isObject(arg1) || Parse._isNullOrUndefined(arg1)) { + attrs = arg1; + options = arg2; + } else { + attrs = {}; + attrs[arg1] = arg2; + options = arg3; + } + + // Make save({ success: function() {} }) work. + if (!options && attrs) { + var extra_keys = _.reject(attrs, function(value, key) { + return _.include(["success", "error", "wait"], key); + }); + if (extra_keys.length === 0) { + var all_functions = true; + if (_.has(attrs, "success") && !_.isFunction(attrs.success)) { + all_functions = false; + } + if (_.has(attrs, "error") && !_.isFunction(attrs.error)) { + all_functions = false; + } + if (all_functions) { + // This attrs object looks like it's really an options object, + // and there's no other options object, so let's just use it. + return this.save(null, attrs); + } + } + } + + options = _.clone(options) || {}; + if (options.wait) { + current = _.clone(this.attributes); + } + + var setOptions = _.clone(options) || {}; + if (setOptions.wait) { + setOptions.silent = true; + } + var setError; + setOptions.error = function(model, error) { + setError = error; + }; + if (attrs && !this.set(attrs, setOptions)) { + return Parse.Promise.error(setError)._thenRunCallbacks(options, this); + } + + var model = this; + + // If there is any unsaved child, save it first. + model._refreshCache(); + + + + var unsavedChildren = []; + var unsavedFiles = []; + Parse.Object._findUnsavedChildren(model.attributes, + unsavedChildren, + unsavedFiles); + if (unsavedChildren.length + unsavedFiles.length > 0) { + return Parse.Object._deepSaveAsync(this.attributes, { + useMasterKey: options.useMasterKey, + sessionToken: options.sessionToken + }).then(function() { + return model.save(null, options); + }, function(error) { + return Parse.Promise.error(error)._thenRunCallbacks(options, model); + }); + } + + this._startSave(); + this._saving = (this._saving || 0) + 1; + + this._allPreviousSaves = this._allPreviousSaves || Parse.Promise.as(); + this._allPreviousSaves = this._allPreviousSaves._continueWith(function() { + var method = model.id ? 'PUT' : 'POST'; + + var json = model._getSaveJSON(); + + var route = "classes"; + var className = model.className; + if (model.className === "_User" && !model.id) { + // Special-case user sign-up. + route = "users"; + className = null; + } + var request = Parse._request({ + route: route, + className: className, + objectId: model.id, + method: method, + useMasterKey: options.useMasterKey, + sessionToken: options.sessionToken, + data: json + }); + + request = request.then(function(resp, status, xhr) { + var serverAttrs = model.parse(resp, status, xhr); + if (options.wait) { + serverAttrs = _.extend(attrs || {}, serverAttrs); + } + model._finishSave(serverAttrs); + if (options.wait) { + model.set(current, setOptions); + } + return model; + + }, function(error) { + model._cancelSave(); + return Parse.Promise.error(error); + + })._thenRunCallbacks(options, model); + + return request; + }); + return this._allPreviousSaves; + }, + + /** + * Destroy this model on the server if it was already persisted. + * Optimistically removes the model from its collection, if it has one. + * If `wait: true` is passed, waits for the server to respond + * before removal. + * + * @param {Object} options A Backbone-style callback object. + * Valid options are:
    + *
  • wait: Set to true to wait for the server to confirm successful + * deletion of the object before triggering the `destroy` event. + *
  • success: A Backbone-style success callback + *
  • error: An Backbone-style error callback. + *
  • useMasterKey: In Cloud Code and Node only, causes the Master Key to + * be used for this request. + *
  • sessionToken: A valid session token, used for making a request on + * behalf of a specific user. + *
+ * @return {Parse.Promise} A promise that is fulfilled when the destroy + * completes. + */ + destroy: function(options) { + options = options || {}; + var model = this; + + var triggerDestroy = function() { + model.trigger('destroy', model, model.collection, options); + }; + + if (!this.id) { + return triggerDestroy(); + } + + if (!options.wait) { + triggerDestroy(); + } + + var request = Parse._request({ + route: "classes", + className: this.className, + objectId: this.id, + method: 'DELETE', + useMasterKey: options.useMasterKey, + sessionToken: options.sessionToken + }); + return request.then(function() { + if (options.wait) { + triggerDestroy(); + } + return model; + })._thenRunCallbacks(options, this); + }, + + /** + * Converts a response into the hash of attributes to be set on the model. + * @ignore + */ + parse: function(resp, status, xhr) { + var output = _.clone(resp); + _(["createdAt", "updatedAt"]).each(function(key) { + if (output[key]) { + output[key] = Parse._parseDate(output[key]); + } + }); + if (!output.updatedAt) { + output.updatedAt = output.createdAt; + } + if (status) { + this._existed = (status !== 201); + } + return output; + }, + + /** + * Creates a new model with identical attributes to this one. + * @return {Parse.Object} + */ + clone: function() { + return new this.constructor(this.attributes); + }, + + /** + * Returns true if this object has never been saved to Parse. + * @return {Boolean} + */ + isNew: function() { + return !this.id; + }, + + /** + * Call this method to manually fire a `"change"` event for this model and + * a `"change:attribute"` event for each changed attribute. + * Calling this will cause all objects observing the model to update. + */ + change: function(options) { + options = options || {}; + var changing = this._changing; + this._changing = true; + + // Silent changes become pending changes. + var self = this; + Parse._objectEach(this._silent, function(attr) { + self._pending[attr] = true; + }); + + // Silent changes are triggered. + var changes = _.extend({}, options.changes, this._silent); + this._silent = {}; + Parse._objectEach(changes, function(unused_value, attr) { + self.trigger('change:' + attr, self, self.get(attr), options); + }); + if (changing) { + return this; + } + + // This is to get around lint not letting us make a function in a loop. + var deleteChanged = function(value, attr) { + if (!self._pending[attr] && !self._silent[attr]) { + delete self.changed[attr]; + } + }; + + // Continue firing `"change"` events while there are pending changes. + while (!_.isEmpty(this._pending)) { + this._pending = {}; + this.trigger('change', this, options); + // Pending and silent changes still remain. + Parse._objectEach(this.changed, deleteChanged); + self._previousAttributes = _.clone(this.attributes); + } + + this._changing = false; + return this; + }, + + /** + * Returns true if this object was created by the Parse server when the + * object might have already been there (e.g. in the case of a Facebook + * login) + */ + existed: function() { + return this._existed; + }, + + /** + * Determine if the model has changed since the last "change" + * event. If you specify an attribute name, determine if that attribute + * has changed. + * @param {String} attr Optional attribute name + * @return {Boolean} + */ + hasChanged: function(attr) { + if (!arguments.length) { + return !_.isEmpty(this.changed); + } + return this.changed && _.has(this.changed, attr); + }, + + /** + * Returns an object containing all the attributes that have changed, or + * false if there are no changed attributes. Useful for determining what + * parts of a view need to be updated and/or what attributes need to be + * persisted to the server. Unset attributes will be set to undefined. + * You can also pass an attributes object to diff against the model, + * determining if there *would be* a change. + */ + changedAttributes: function(diff) { + if (!diff) { + return this.hasChanged() ? _.clone(this.changed) : false; + } + var changed = {}; + var old = this._previousAttributes; + Parse._objectEach(diff, function(diffVal, attr) { + if (!_.isEqual(old[attr], diffVal)) { + changed[attr] = diffVal; + } + }); + return changed; + }, + + /** + * Gets the previous value of an attribute, recorded at the time the last + * "change" event was fired. + * @param {String} attr Name of the attribute to get. + */ + previous: function(attr) { + if (!arguments.length || !this._previousAttributes) { + return null; + } + return this._previousAttributes[attr]; + }, + + /** + * Gets all of the attributes of the model at the time of the previous + * "change" event. + * @return {Object} + */ + previousAttributes: function() { + return _.clone(this._previousAttributes); + }, + + /** + * Checks if the model is currently in a valid state. It's only possible to + * get into an *invalid* state if you're using silent changes. + * @return {Boolean} + */ + isValid: function() { + return !this.validate(this.attributes); + }, + + /** + * You should not call this function directly unless you subclass + * Parse.Object, in which case you can override this method + * to provide additional validation on set and + * save. Your implementation should return + * + * @param {Object} attrs The current data to validate. + * @param {Object} options A Backbone-like options object. + * @return {} False if the data is valid. An error object otherwise. + * @see Parse.Object#set + */ + validate: function(attrs, options) { + if (_.has(attrs, "ACL") && !(attrs.ACL instanceof Parse.ACL)) { + return new Parse.Error(Parse.Error.OTHER_CAUSE, + "ACL must be a Parse.ACL."); + } + var correct = true; + Parse._objectEach(attrs, function(unused_value, key) { + if (!(/^[A-Za-z][0-9A-Za-z_]*$/).test(key)) { + correct = false; + } + }); + if (!correct) { + return new Parse.Error(Parse.Error.INVALID_KEY_NAME); + } + return false; + }, + + /** + * Run validation against a set of incoming attributes, returning `true` + * if all is well. If a specific `error` callback has been passed, + * call that instead of firing the general `"error"` event. + */ + _validate: function(attrs, options) { + if (options.silent || !this.validate) { + return true; + } + attrs = _.extend({}, this.attributes, attrs); + var error = this.validate(attrs, options); + if (!error) { + return true; + } + if (options && options.error) { + options.error(this, error, options); + } else { + this.trigger('error', this, error, options); + } + return false; + }, + + /** + * Returns the ACL for this object. + * @returns {Parse.ACL} An instance of Parse.ACL. + * @see Parse.Object#get + */ + getACL: function() { + return this.get("ACL"); + }, + + /** + * Sets the ACL to be used for this object. + * @param {Parse.ACL} acl An instance of Parse.ACL. + * @param {Object} options Optional Backbone-like options object to be + * passed in to set. + * @return {Boolean} Whether the set passed validation. + * @see Parse.Object#set + */ + setACL: function(acl, options) { + return this.set("ACL", acl, options); + } + + }); + + /** + * Returns the appropriate subclass for making new instances of the given + * className string. + */ + Parse.Object._getSubclass = function(className) { + if (!_.isString(className)) { + throw "Parse.Object._getSubclass requires a string argument."; + } + var ObjectClass = Parse.Object._classMap[className]; + if (!ObjectClass) { + ObjectClass = Parse.Object.extend(className); + Parse.Object._classMap[className] = ObjectClass; + } + return ObjectClass; + }; + + /** + * Creates an instance of a subclass of Parse.Object for the given classname. + */ + Parse.Object._create = function(className, attributes, options) { + var ObjectClass = Parse.Object._getSubclass(className); + return new ObjectClass(attributes, options); + }; + + /** + * Returns a list of object ids given a list of objects. + */ + Parse.Object._toObjectIdArray = function(list, omitObjectsWithData) { + if (list.length === 0) { + return Parse.Promise.as(list); + } + + var error; + var className = list[0].className; + var objectIds = []; + for (var i = 0; i < list.length; i++) { + var object = list[i]; + if (className !== object.className) { + error = new Parse.Error(Parse.Error.INVALID_CLASS_NAME, + "All objects should be of the same class"); + return Parse.Promise.error(error); + } else if (!object.id) { + error = new Parse.Error(Parse.Error.MISSING_OBJECT_ID, + "All objects must have an ID"); + return Parse.Promise.error(error); + } else if (omitObjectsWithData && object._hasData) { + continue; + } + objectIds.push(object.id); + } + + return Parse.Promise.as(objectIds); + }; + + /** + * Updates a list of objects with fetched results. + */ + Parse.Object._updateWithFetchedResults = function(list, fetched, forceFetch) { + var fetchedObjectsById = {}; + Parse._arrayEach(fetched, function(object, i) { + fetchedObjectsById[object.id] = object; + }); + + for (var i = 0; i < list.length; i++) { + var object = list[i]; + var fetchedObject = fetchedObjectsById[object.id]; + if (!fetchedObject && forceFetch) { + var error = new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, + "All objects must exist on the server"); + return Parse.Promise.error(error); + } + + object._mergeFromObject(fetchedObject); + } + + return Parse.Promise.as(list); + }; + + /** + * Fetches the objects given in list. The forceFetch option will fetch all + * objects if true and ignore objects with data if false. + */ + Parse.Object._fetchAll = function(list, forceFetch) { + if (list.length === 0) { + return Parse.Promise.as(list); + } + + var omitObjectsWithData = !forceFetch; + return Parse.Object._toObjectIdArray( + list, + omitObjectsWithData + ).then(function(objectIds) { + var className = list[0].className; + var query = new Parse.Query(className); + query.containedIn("objectId", objectIds); + query.limit = objectIds.length; + return query.find(); + }).then(function(results) { + return Parse.Object._updateWithFetchedResults( + list, + results, + forceFetch + ); + }); + }; + + // Set up a map of className to class so that we can create new instances of + // Parse Objects from JSON automatically. + Parse.Object._classMap = {}; + + Parse.Object._extend = Parse._extend; + + /** + * Creates a new subclass of Parse.Object for the given Parse class name. + * + *

Every extension of a Parse class will inherit from the most recent + * previous extension of that class. When a Parse.Object is automatically + * created by parsing JSON, it will use the most recent extension of that + * class.

+ * + *

You should call either:

+   *     var MyClass = Parse.Object.extend("MyClass", {
+   *         Instance methods,
+   *         initialize: function(attrs, options) {
+   *             this.someInstanceProperty = [],
+   *             Other instance properties
+   *         }
+   *     }, {
+   *         Class properties
+   *     });
+ * or, for Backbone compatibility:
+   *     var MyClass = Parse.Object.extend({
+   *         className: "MyClass",
+   *         Instance methods,
+   *         initialize: function(attrs, options) {
+   *             this.someInstanceProperty = [],
+   *             Other instance properties
+   *         }
+   *     }, {
+   *         Class properties
+   *     });

+ * + * @param {String} className The name of the Parse class backing this model. + * @param {Object} protoProps Instance properties to add to instances of the + * class returned from this method. + * @param {Object} classProps Class properties to add the class returned from + * this method. + * @return {Class} A new subclass of Parse.Object. + */ + Parse.Object.extend = function(className, protoProps, classProps) { + // Handle the case with only two args. + if (!_.isString(className)) { + if (className && _.has(className, "className")) { + return Parse.Object.extend(className.className, className, protoProps); + } else { + throw new Error( + "Parse.Object.extend's first argument should be the className."); + } + } + + // If someone tries to subclass "User", coerce it to the right type. + if (className === "User" && Parse.User._performUserRewrite) { + className = "_User"; + } + protoProps = protoProps || {}; + protoProps.className = className; + + var NewClassObject = null; + if (_.has(Parse.Object._classMap, className)) { + var OldClassObject = Parse.Object._classMap[className]; + // This new subclass has been told to extend both from "this" and from + // OldClassObject. This is multiple inheritance, which isn't supported. + // For now, let's just pick one. + NewClassObject = OldClassObject._extend(protoProps, classProps); + } else { + NewClassObject = this._extend(protoProps, classProps); + } + // Extending a subclass should reuse the classname automatically. + NewClassObject.extend = function(arg0) { + if (_.isString(arg0) || (arg0 && _.has(arg0, "className"))) { + return Parse.Object.extend.apply(NewClassObject, arguments); + } + var newArguments = [className].concat(Parse._.toArray(arguments)); + return Parse.Object.extend.apply(NewClassObject, newArguments); + }; + + /** + * Creates a reference to a subclass of Parse.Object with the given id. This + * does not exist on Parse.Object, only on subclasses. + * + *

A shortcut for:

+     *  var Foo = Parse.Object.extend("Foo");
+     *  var pointerToFoo = new Foo();
+     *  pointerToFoo.id = "myObjectId";
+     * 
+ * + * @name createWithoutData + * @param {String} id The ID of the object to create a reference to. + * @return {Parse.Object} A Parse.Object reference. + * @function + * @memberOf Parse.Object + */ + NewClassObject.createWithoutData = function(id) { + var obj = new NewClassObject(); + obj.id = id; + return obj; + }; + + Parse.Object._classMap[className] = NewClassObject; + return NewClassObject; + }; + + Parse.Object._findUnsavedChildren = function(object, children, files) { + Parse._traverse(object, function(object) { + if (object instanceof Parse.Object) { + object._refreshCache(); + if (object.dirty()) { + children.push(object); + } + return; + } + + if (object instanceof Parse.File) { + if (!object.url()) { + files.push(object); + } + return; + } + }); + }; + + Parse.Object._canBeSerializedAsValue = function(object) { + + if (object instanceof Parse.Object) { + return !!object.id; + } + if (object instanceof Parse.File) { + // Don't recurse indefinitely into files. + return true; + } + + var canBeSerializedAsValue = true; + + if (_.isArray(object)) { + Parse._arrayEach(object, function(child) { + if (!Parse.Object._canBeSerializedAsValue(child)) { + canBeSerializedAsValue = false; + } + }); + } else if (_.isObject(object)) { + Parse._objectEach(object, function(child) { + if (!Parse.Object._canBeSerializedAsValue(child)) { + canBeSerializedAsValue = false; + } + }); + } + return canBeSerializedAsValue; + }; + + /** + * @param {Object} object The root object. + * @param {Object} options: The only valid option is useMasterKey. + */ + Parse.Object._deepSaveAsync = function(object, options) { + var unsavedChildren = []; + var unsavedFiles = []; + Parse.Object._findUnsavedChildren(object, unsavedChildren, unsavedFiles); + + var promise = Parse.Promise.as(); + _.each(unsavedFiles, function(file) { + promise = promise.then(function() { + return file.save(options); + }); + }); + + var objects = _.uniq(unsavedChildren); + var remaining = _.uniq(objects); + + return promise.then(function() { + return Parse.Promise._continueWhile(function() { + return remaining.length > 0; + }, function() { + + // Gather up all the objects that can be saved in this batch. + var batch = []; + var newRemaining = []; + Parse._arrayEach(remaining, function(object) { + // Limit batches to 20 objects. + if (batch.length > 20) { + newRemaining.push(object); + return; + } + + if (object._canBeSerialized()) { + batch.push(object); + } else { + newRemaining.push(object); + } + }); + remaining = newRemaining; + + // If we can't save any objects, there must be a circular reference. + if (batch.length === 0) { + return Parse.Promise.error( + new Parse.Error(Parse.Error.OTHER_CAUSE, + "Tried to save a batch with a cycle.")); + } + + // Reserve a spot in every object's save queue. + var readyToStart = Parse.Promise.when(_.map(batch, function(object) { + return object._allPreviousSaves || Parse.Promise.as(); + })); + var batchFinished = new Parse.Promise(); + Parse._arrayEach(batch, function(object) { + object._allPreviousSaves = batchFinished; + }); + + // Save a single batch, whether previous saves succeeded or failed. + return readyToStart._continueWith(function() { + return Parse._request({ + route: "batch", + method: "POST", + useMasterKey: options.useMasterKey, + sessionToken: options.sessionToken, + data: { + requests: _.map(batch, function(object) { + var json = object._getSaveJSON(); + var method = "POST"; + + var path = "/1/classes/" + object.className; + if (object.id) { + path = path + "/" + object.id; + method = "PUT"; + } + + object._startSave(); + + return { + method: method, + path: path, + body: json + }; + }) + } + }).then(function(response, status, xhr) { + var error; + Parse._arrayEach(batch, function(object, i) { + if (response[i].success) { + object._finishSave( + object.parse(response[i].success, status, xhr)); + } else { + error = error || response[i].error; + object._cancelSave(); + } + }); + if (error) { + return Parse.Promise.error( + new Parse.Error(error.code, error.error)); + } + + }).then(function(results) { + batchFinished.resolve(results); + return results; + }, function(error) { + batchFinished.reject(error); + return Parse.Promise.error(error); + }); + }); + }); + }).then(function() { + return object; + }); + }; + +}(this)); + +(function(root) { + root.Parse = root.Parse || {}; + var Parse = root.Parse; + var _ = Parse._; + + /** + * Represents a Role on the Parse server. Roles represent groupings of + * Users for the purposes of granting permissions (e.g. specifying an ACL + * for an Object). Roles are specified by their sets of child users and + * child roles, all of which are granted any permissions that the parent + * role has. + * + *

Roles must have a name (which cannot be changed after creation of the + * role), and must specify an ACL.

+ * @class + * A Parse.Role is a local representation of a role persisted to the Parse + * cloud. + */ + Parse.Role = Parse.Object.extend("_Role", /** @lends Parse.Role.prototype */ { + // Instance Methods + + /** + * Constructs a new ParseRole with the given name and ACL. + * + * @param {String} name The name of the Role to create. + * @param {Parse.ACL} acl The ACL for this role. Roles must have an ACL. + */ + constructor: function(name, acl) { + if (_.isString(name) && (acl instanceof Parse.ACL)) { + Parse.Object.prototype.constructor.call(this, null, null); + this.setName(name); + this.setACL(acl); + } else { + Parse.Object.prototype.constructor.call(this, name, acl); + } + }, + + /** + * Gets the name of the role. You can alternatively call role.get("name") + * + * @return {String} the name of the role. + */ + getName: function() { + return this.get("name"); + }, + + /** + * Sets the name for a role. This value must be set before the role has + * been saved to the server, and cannot be set once the role has been + * saved. + * + *

+ * A role's name can only contain alphanumeric characters, _, -, and + * spaces. + *

+ * + *

This is equivalent to calling role.set("name", name)

+ * + * @param {String} name The name of the role. + * @param {Object} options Standard options object with success and error + * callbacks. + */ + setName: function(name, options) { + return this.set("name", name, options); + }, + + /** + * Gets the Parse.Relation for the Parse.Users that are direct + * children of this role. These users are granted any privileges that this + * role has been granted (e.g. read or write access through ACLs). You can + * add or remove users from the role through this relation. + * + *

This is equivalent to calling role.relation("users")

+ * + * @return {Parse.Relation} the relation for the users belonging to this + * role. + */ + getUsers: function() { + return this.relation("users"); + }, + + /** + * Gets the Parse.Relation for the Parse.Roles that are direct + * children of this role. These roles' users are granted any privileges that + * this role has been granted (e.g. read or write access through ACLs). You + * can add or remove child roles from this role through this relation. + * + *

This is equivalent to calling role.relation("roles")

+ * + * @return {Parse.Relation} the relation for the roles belonging to this + * role. + */ + getRoles: function() { + return this.relation("roles"); + }, + + /** + * @ignore + */ + validate: function(attrs, options) { + if ("name" in attrs && attrs.name !== this.getName()) { + var newName = attrs.name; + if (this.id && this.id !== attrs.objectId) { + // Check to see if the objectId being set matches this.id. + // This happens during a fetch -- the id is set before calling fetch. + // Let the name be set in this case. + return new Parse.Error(Parse.Error.OTHER_CAUSE, + "A role's name can only be set before it has been saved."); + } + if (!_.isString(newName)) { + return new Parse.Error(Parse.Error.OTHER_CAUSE, + "A role's name must be a String."); + } + if (!(/^[0-9a-zA-Z\-_ ]+$/).test(newName)) { + return new Parse.Error(Parse.Error.OTHER_CAUSE, + "A role's name can only contain alphanumeric characters, _," + + " -, and spaces."); + } + } + if (Parse.Object.prototype.validate) { + return Parse.Object.prototype.validate.call(this, attrs, options); + } + return false; + } + }); +}(this)); + + +/*global _: false */ +(function(root) { + root.Parse = root.Parse || {}; + var Parse = root.Parse; + var _ = Parse._; + + /** + * Creates a new instance with the given models and options. Typically, you + * will not call this method directly, but will instead make a subclass using + * Parse.Collection.extend. + * + * @param {Array} models An array of instances of Parse.Object. + * + * @param {Object} options An optional object with Backbone-style options. + * Valid options are:
    + *
  • model: The Parse.Object subclass that this collection contains. + *
  • query: An instance of Parse.Query to use when fetching items. + *
  • comparator: A string property name or function to sort by. + *
+ * + * @see Parse.Collection.extend + * + * @class + * + *

Provides a standard collection class for our sets of models, ordered + * or unordered. For more information, see the + * Backbone + * documentation.

+ */ + Parse.Collection = function(models, options) { + options = options || {}; + if (options.comparator) { + this.comparator = options.comparator; + } + if (options.model) { + this.model = options.model; + } + if (options.query) { + this.query = options.query; + } + this._reset(); + this.initialize.apply(this, arguments); + if (models) { + this.reset(models, {silent: true, parse: options.parse}); + } + }; + + // Define the Collection's inheritable methods. + _.extend(Parse.Collection.prototype, Parse.Events, + /** @lends Parse.Collection.prototype */ { + + // The default model for a collection is just a Parse.Object. + // This should be overridden in most cases. + + model: Parse.Object, + + /** + * Initialize is an empty function by default. Override it with your own + * initialization logic. + */ + initialize: function(){}, + + /** + * The JSON representation of a Collection is an array of the + * models' attributes. + */ + toJSON: function() { + return this.map(function(model){ return model.toJSON(); }); + }, + + /** + * Add a model, or list of models to the set. Pass **silent** to avoid + * firing the `add` event for every new model. + * + * @param {Array} models An array of instances of Parse.Object. + * + * @param {Object} options An optional object with Backbone-style options. + * Valid options are:
    + *
  • at: The index at which to add the models. + *
  • silent: Set to true to avoid firing the `add` event for every new + * model. + *
+ */ + add: function(models, options) { + var i, index, length, model, cid, id, cids = {}, ids = {}; + options = options || {}; + models = _.isArray(models) ? models.slice() : [models]; + + // Begin by turning bare objects into model references, and preventing + // invalid models or duplicate models from being added. + for (i = 0, length = models.length; i < length; i++) { + models[i] = this._prepareModel(models[i], options); + model = models[i]; + if (!model) { + throw new Error("Can't add an invalid model to a collection"); + } + cid = model.cid; + if (cids[cid] || this._byCid[cid]) { + throw new Error("Duplicate cid: can't add the same model " + + "to a collection twice"); + } + id = model.id; + if (!Parse._isNullOrUndefined(id) && (ids[id] || this._byId[id])) { + throw new Error("Duplicate id: can't add the same model " + + "to a collection twice"); + } + ids[id] = model; + cids[cid] = model; + } + + // Listen to added models' events, and index models for lookup by + // `id` and by `cid`. + for (i = 0; i < length; i++) { + (model = models[i]).on('all', this._onModelEvent, this); + this._byCid[model.cid] = model; + if (model.id) { + this._byId[model.id] = model; + } + } + + // Insert models into the collection, re-sorting if needed, and triggering + // `add` events unless silenced. + this.length += length; + index = Parse._isNullOrUndefined(options.at) ? + this.models.length : options.at; + this.models.splice.apply(this.models, [index, 0].concat(models)); + if (this.comparator) { + this.sort({silent: true}); + } + if (options.silent) { + return this; + } + for (i = 0, length = this.models.length; i < length; i++) { + model = this.models[i]; + if (cids[model.cid]) { + options.index = i; + model.trigger('add', model, this, options); + } + } + return this; + }, + + /** + * Remove a model, or a list of models from the set. Pass silent to avoid + * firing the remove event for every model removed. + * + * @param {Array} models The model or list of models to remove from the + * collection. + * @param {Object} options An optional object with Backbone-style options. + * Valid options are:
    + *
  • silent: Set to true to avoid firing the `remove` event. + *
+ */ + remove: function(models, options) { + var i, l, index, model; + options = options || {}; + models = _.isArray(models) ? models.slice() : [models]; + for (i = 0, l = models.length; i < l; i++) { + model = this.getByCid(models[i]) || this.get(models[i]); + if (!model) { + continue; + } + delete this._byId[model.id]; + delete this._byCid[model.cid]; + index = this.indexOf(model); + this.models.splice(index, 1); + this.length--; + if (!options.silent) { + options.index = index; + model.trigger('remove', model, this, options); + } + this._removeReference(model); + } + return this; + }, + + /** + * Gets a model from the set by id. + * @param {String} id The Parse objectId identifying the Parse.Object to + * fetch from this collection. + */ + get: function(id) { + return id && this._byId[id.id || id]; + }, + + /** + * Gets a model from the set by client id. + * @param {} cid The Backbone collection id identifying the Parse.Object to + * fetch from this collection. + */ + getByCid: function(cid) { + return cid && this._byCid[cid.cid || cid]; + }, + + /** + * Gets the model at the given index. + * + * @param {Number} index The index of the model to return. + */ + at: function(index) { + return this.models[index]; + }, + + /** + * Forces the collection to re-sort itself. You don't need to call this + * under normal circumstances, as the set will maintain sort order as each + * item is added. + * @param {Object} options An optional object with Backbone-style options. + * Valid options are:
    + *
  • silent: Set to true to avoid firing the `reset` event. + *
+ */ + sort: function(options) { + options = options || {}; + if (!this.comparator) { + throw new Error('Cannot sort a set without a comparator'); + } + var boundComparator = _.bind(this.comparator, this); + if (this.comparator.length === 1) { + this.models = this.sortBy(boundComparator); + } else { + this.models.sort(boundComparator); + } + if (!options.silent) { + this.trigger('reset', this, options); + } + return this; + }, + + /** + * Plucks an attribute from each model in the collection. + * @param {String} attr The attribute to return from each model in the + * collection. + */ + pluck: function(attr) { + return _.map(this.models, function(model){ return model.get(attr); }); + }, + + /** + * When you have more items than you want to add or remove individually, + * you can reset the entire set with a new list of models, without firing + * any `add` or `remove` events. Fires `reset` when finished. + * + * @param {Array} models The model or list of models to remove from the + * collection. + * @param {Object} options An optional object with Backbone-style options. + * Valid options are:
    + *
  • silent: Set to true to avoid firing the `reset` event. + *
+ */ + reset: function(models, options) { + var self = this; + models = models || []; + options = options || {}; + Parse._arrayEach(this.models, function(model) { + self._removeReference(model); + }); + this._reset(); + this.add(models, {silent: true, parse: options.parse}); + if (!options.silent) { + this.trigger('reset', this, options); + } + return this; + }, + + /** + * Fetches the default set of models for this collection, resetting the + * collection when they arrive. If `add: true` is passed, appends the + * models to the collection instead of resetting. + * + * @param {Object} options An optional object with Backbone-style options. + * Valid options are:
    + *
  • silent: Set to true to avoid firing `add` or `reset` events for + * models fetched by this fetch. + *
  • success: A Backbone-style success callback. + *
  • error: An Backbone-style error callback. + *
  • useMasterKey: In Cloud Code and Node only, uses the Master Key for + * this request. + *
  • sessionToken: A valid session token, used for making a request on + * behalf of a specific user. + *
+ */ + fetch: function(options) { + options = _.clone(options) || {}; + if (options.parse === undefined) { + options.parse = true; + } + var collection = this; + var query = this.query || new Parse.Query(this.model); + return query.find({ + useMasterKey: options.useMasterKey, + sessionToken: options.sessionToken + }).then(function(results) { + if (options.add) { + collection.add(results, options); + } else { + collection.reset(results, options); + } + return collection; + })._thenRunCallbacks(options, this); + }, + + /** + * Creates a new instance of a model in this collection. Add the model to + * the collection immediately, unless `wait: true` is passed, in which case + * we wait for the server to agree. + * + * @param {Parse.Object} model The new model to create and add to the + * collection. + * @param {Object} options An optional object with Backbone-style options. + * Valid options are:
    + *
  • wait: Set to true to wait for the server to confirm creation of the + * model before adding it to the collection. + *
  • silent: Set to true to avoid firing an `add` event. + *
  • success: A Backbone-style success callback. + *
  • error: An Backbone-style error callback. + *
  • useMasterKey: In Cloud Code and Node only, uses the Master Key for + * this request. + *
  • sessionToken: A valid session token, used for making a request on + * behalf of a specific user. + *
+ */ + create: function(model, options) { + var coll = this; + options = options ? _.clone(options) : {}; + model = this._prepareModel(model, options); + if (!model) { + return false; + } + if (!options.wait) { + coll.add(model, options); + } + var success = options.success; + options.success = function(nextModel, resp, xhr) { + if (options.wait) { + coll.add(nextModel, options); + } + if (success) { + success(nextModel, resp); + } else { + nextModel.trigger('sync', model, resp, options); + } + }; + model.save(null, options); + return model; + }, + + /** + * Converts a response into a list of models to be added to the collection. + * The default implementation is just to pass it through. + * @ignore + */ + parse: function(resp, xhr) { + return resp; + }, + + /** + * Proxy to _'s chain. Can't be proxied the same way the rest of the + * underscore methods are proxied because it relies on the underscore + * constructor. + */ + chain: function() { + return _(this.models).chain(); + }, + + /** + * Reset all internal state. Called when the collection is reset. + */ + _reset: function(options) { + this.length = 0; + this.models = []; + this._byId = {}; + this._byCid = {}; + }, + + /** + * Prepare a model or hash of attributes to be added to this collection. + */ + _prepareModel: function(model, options) { + if (!(model instanceof Parse.Object)) { + var attrs = model; + options.collection = this; + model = new this.model(attrs, options); + if (!model._validate(model.attributes, options)) { + model = false; + } + } else if (!model.collection) { + model.collection = this; + } + return model; + }, + + /** + * Internal method to remove a model's ties to a collection. + */ + _removeReference: function(model) { + if (this === model.collection) { + delete model.collection; + } + model.off('all', this._onModelEvent, this); + }, + + /** + * Internal method called every time a model in the set fires an event. + * Sets need to update their indexes when models change ids. All other + * events simply proxy through. "add" and "remove" events that originate + * in other collections are ignored. + */ + _onModelEvent: function(ev, model, collection, options) { + if ((ev === 'add' || ev === 'remove') && collection !== this) { + return; + } + if (ev === 'destroy') { + this.remove(model, options); + } + if (model && ev === 'change:objectId') { + delete this._byId[model.previous("objectId")]; + this._byId[model.id] = model; + } + this.trigger.apply(this, arguments); + } + + }); + + // Underscore methods that we want to implement on the Collection. + var methods = ['forEach', 'each', 'map', 'reduce', 'reduceRight', 'find', + 'detect', 'filter', 'select', 'reject', 'every', 'all', 'some', 'any', + 'include', 'contains', 'invoke', 'max', 'min', 'sortBy', 'sortedIndex', + 'toArray', 'size', 'first', 'initial', 'rest', 'last', 'without', 'indexOf', + 'shuffle', 'lastIndexOf', 'isEmpty', 'groupBy']; + + // Mix in each Underscore method as a proxy to `Collection#models`. + Parse._arrayEach(methods, function(method) { + Parse.Collection.prototype[method] = function() { + return _[method].apply(_, [this.models].concat(_.toArray(arguments))); + }; + }); + + /** + * Creates a new subclass of Parse.Collection. For example,
+   *   var MyCollection = Parse.Collection.extend({
+   *     // Instance properties
+   *
+   *     model: MyClass,
+   *     query: MyQuery,
+   *
+   *     getFirst: function() {
+   *       return this.at(0);
+   *     }
+   *   }, {
+   *     // Class properties
+   *
+   *     makeOne: function() {
+   *       return new MyCollection();
+   *     }
+   *   });
+   *
+   *   var collection = new MyCollection();
+   * 
+ * + * @function + * @param {Object} instanceProps Instance properties for the collection. + * @param {Object} classProps Class properies for the collection. + * @return {Class} A new subclass of Parse.Collection. + */ + Parse.Collection.extend = Parse._extend; + +}(this)); + +/*global _: false, document: false */ +(function(root) { + root.Parse = root.Parse || {}; + var Parse = root.Parse; + var _ = Parse._; + + /** + * Creating a Parse.View creates its initial element outside of the DOM, + * if an existing element is not provided... + * @class + * + *

A fork of Backbone.View, provided for your convenience. If you use this + * class, you must also include jQuery, or another library that provides a + * jQuery-compatible $ function. For more information, see the + * Backbone + * documentation.

+ *

Available in the client SDK only.

+ */ + Parse.View = function(options) { + this.cid = _.uniqueId('view'); + this._configure(options || {}); + this._ensureElement(); + this.initialize.apply(this, arguments); + this.delegateEvents(); + }; + + // Cached regex to split keys for `delegate`. + var eventSplitter = /^(\S+)\s*(.*)$/; + + // List of view options to be merged as properties. + + var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', + 'className', 'tagName']; + + // Set up all inheritable **Parse.View** properties and methods. + _.extend(Parse.View.prototype, Parse.Events, + /** @lends Parse.View.prototype */ { + + // The default `tagName` of a View's element is `"div"`. + tagName: 'div', + + /** + * jQuery delegate for element lookup, scoped to DOM elements within the + * current view. This should be prefered to global lookups where possible. + */ + $: function(selector) { + return this.$el.find(selector); + }, + + /** + * Initialize is an empty function by default. Override it with your own + * initialization logic. + */ + initialize: function(){}, + + /** + * The core function that your view should override, in order + * to populate its element (`this.el`), with the appropriate HTML. The + * convention is for **render** to always return `this`. + */ + render: function() { + return this; + }, + + /** + * Remove this view from the DOM. Note that the view isn't present in the + * DOM by default, so calling this method may be a no-op. + */ + remove: function() { + this.$el.remove(); + return this; + }, + + /** + * For small amounts of DOM Elements, where a full-blown template isn't + * needed, use **make** to manufacture elements, one at a time. + *
+     *     var el = this.make('li', {'class': 'row'},
+     *                        this.model.escape('title'));
+ */ + make: function(tagName, attributes, content) { + var el = document.createElement(tagName); + if (attributes) { + Parse.$(el).attr(attributes); + } + if (content) { + Parse.$(el).html(content); + } + return el; + }, + + /** + * Changes the view's element (`this.el` property), including event + * re-delegation. + */ + setElement: function(element, delegate) { + this.$el = Parse.$(element); + this.el = this.$el[0]; + if (delegate !== false) { + this.delegateEvents(); + } + return this; + }, + + /** + * Set callbacks. this.events is a hash of + *
+     * *{"event selector": "callback"}*
+     *
+     *     {
+     *       'mousedown .title':  'edit',
+     *       'click .button':     'save'
+     *       'click .open':       function(e) { ... }
+     *     }
+     * 
+ * pairs. Callbacks will be bound to the view, with `this` set properly. + * Uses event delegation for efficiency. + * Omitting the selector binds the event to `this.el`. + * This only works for delegate-able events: not `focus`, `blur`, and + * not `change`, `submit`, and `reset` in Internet Explorer. + */ + delegateEvents: function(events) { + events = events || Parse._getValue(this, 'events'); + if (!events) { + return; + } + this.undelegateEvents(); + var self = this; + Parse._objectEach(events, function(method, key) { + if (!_.isFunction(method)) { + method = self[events[key]]; + } + if (!method) { + throw new Error('Event "' + events[key] + '" does not exist'); + } + var match = key.match(eventSplitter); + var eventName = match[1], selector = match[2]; + method = _.bind(method, self); + eventName += '.delegateEvents' + self.cid; + if (selector === '') { + self.$el.bind(eventName, method); + } else { + self.$el.delegate(selector, eventName, method); + } + }); + }, + + /** + * Clears all callbacks previously bound to the view with `delegateEvents`. + * You usually don't need to use this, but may wish to if you have multiple + * Backbone views attached to the same DOM element. + */ + undelegateEvents: function() { + this.$el.unbind('.delegateEvents' + this.cid); + }, + + /** + * Performs the initial configuration of a View with a set of options. + * Keys with special meaning *(model, collection, id, className)*, are + * attached directly to the view. + */ + _configure: function(options) { + if (this.options) { + options = _.extend({}, this.options, options); + } + var self = this; + _.each(viewOptions, function(attr) { + if (options[attr]) { + self[attr] = options[attr]; + } + }); + this.options = options; + }, + + /** + * Ensure that the View has a DOM element to render into. + * If `this.el` is a string, pass it through `$()`, take the first + * matching element, and re-assign it to `el`. Otherwise, create + * an element from the `id`, `className` and `tagName` properties. + */ + _ensureElement: function() { + if (!this.el) { + var attrs = Parse._getValue(this, 'attributes') || {}; + if (this.id) { + attrs.id = this.id; + } + if (this.className) { + attrs['class'] = this.className; + } + this.setElement(this.make(this.tagName, attrs), false); + } else { + this.setElement(this.el, false); + } + } + + }); + + /** + * @function + * @param {Object} instanceProps Instance properties for the view. + * @param {Object} classProps Class properies for the view. + * @return {Class} A new subclass of Parse.View. + */ + Parse.View.extend = Parse._extend; + +}(this)); + +(function(root) { + root.Parse = root.Parse || {}; + var Parse = root.Parse; + var _ = Parse._; + + /** + * @class + * + *

A Parse.User object is a local representation of a user persisted to the + * Parse cloud. This class is a subclass of a Parse.Object, and retains the + * same functionality of a Parse.Object, but also extends it with various + * user specific methods, like authentication, signing up, and validation of + * uniqueness.

+ */ + Parse.User = Parse.Object.extend("_User", /** @lends Parse.User.prototype */ { + // Instance Variables + _isCurrentUser: false, + + + // Instance Methods + + /** + * Merges another object's attributes into this object. + */ + _mergeFromObject: function(other) { + if (other.getSessionToken()) { + this._sessionToken = other.getSessionToken(); + } + Parse.User.__super__._mergeFromObject.call(this, other); + }, + + /** + * Internal method to handle special fields in a _User response. + */ + _mergeMagicFields: function(attrs) { + if (attrs.sessionToken) { + this._sessionToken = attrs.sessionToken; + delete attrs.sessionToken; + } + Parse.User.__super__._mergeMagicFields.call(this, attrs); + }, + + /** + * Removes null values from authData (which exist temporarily for + * unlinking) + */ + _cleanupAuthData: function() { + if (!this.isCurrent()) { + return; + } + var authData = this.get('authData'); + if (!authData) { + return; + } + Parse._objectEach(this.get('authData'), function(value, key) { + if (!authData[key]) { + delete authData[key]; + } + }); + }, + + /** + * Synchronizes authData for all providers. + */ + _synchronizeAllAuthData: function() { + var authData = this.get('authData'); + if (!authData) { + return; + } + + var self = this; + Parse._objectEach(this.get('authData'), function(value, key) { + self._synchronizeAuthData(key); + }); + }, + + /** + * Synchronizes auth data for a provider (e.g. puts the access token in the + * right place to be used by the Facebook SDK). + */ + _synchronizeAuthData: function(provider) { + if (!this.isCurrent()) { + return; + } + var authType; + if (_.isString(provider)) { + authType = provider; + provider = Parse.User._authProviders[authType]; + } else { + authType = provider.getAuthType(); + } + var authData = this.get('authData'); + if (!authData || !provider) { + return; + } + var success = provider.restoreAuthentication(authData[authType]); + if (!success) { + this._unlinkFrom(provider); + } + }, + + _handleSaveResult: function(makeCurrent) { + // Clean up and synchronize the authData object, removing any unset values + if (makeCurrent) { + this._isCurrentUser = true; + } + this._cleanupAuthData(); + this._synchronizeAllAuthData(); + // Don't keep the password around. + delete this._serverData.password; + this._rebuildEstimatedDataForKey("password"); + this._refreshCache(); + if (makeCurrent || this.isCurrent()) { + Parse.User._saveCurrentUser(this); + } + }, + + /** + * Unlike in the Android/iOS SDKs, logInWith is unnecessary, since you can + * call linkWith on the user (even if it doesn't exist yet on the server). + */ + _linkWith: function(provider, options) { + var authType; + if (_.isString(provider)) { + authType = provider; + provider = Parse.User._authProviders[provider]; + } else { + authType = provider.getAuthType(); + } + if (_.has(options, 'authData')) { + var authData = this.get('authData') || {}; + authData[authType] = options.authData; + this.set('authData', authData); + + // Overridden so that the user can be made the current user. + var newOptions = _.clone(options) || {}; + newOptions.success = function(model) { + model._handleSaveResult(true); + if (options.success) { + options.success.apply(this, arguments); + } + }; + return this.save({'authData': authData}, newOptions); + } else { + var self = this; + var promise = new Parse.Promise(); + provider.authenticate({ + success: function(provider, result) { + self._linkWith(provider, { + authData: result, + success: options.success, + error: options.error + }).then(function() { + promise.resolve(self); + }); + }, + error: function(provider, error) { + if (options.error) { + options.error(self, error); + } + promise.reject(error); + } + }); + return promise; + } + }, + + /** + * Unlinks a user from a service. + */ + _unlinkFrom: function(provider, options) { + var authType; + if (_.isString(provider)) { + authType = provider; + provider = Parse.User._authProviders[provider]; + } else { + authType = provider.getAuthType(); + } + var newOptions = _.clone(options); + var self = this; + newOptions.authData = null; + newOptions.success = function(model) { + self._synchronizeAuthData(provider); + if (options.success) { + options.success.apply(this, arguments); + } + }; + return this._linkWith(provider, newOptions); + }, + + /** + * Checks whether a user is linked to a service. + */ + _isLinked: function(provider) { + var authType; + if (_.isString(provider)) { + authType = provider; + } else { + authType = provider.getAuthType(); + } + var authData = this.get('authData') || {}; + return !!authData[authType]; + }, + + /** + * Deauthenticates all providers. + */ + _logOutWithAll: function() { + var authData = this.get('authData'); + if (!authData) { + return; + } + var self = this; + Parse._objectEach(this.get('authData'), function(value, key) { + self._logOutWith(key); + }); + }, + + /** + * Deauthenticates a single provider (e.g. removing access tokens from the + * Facebook SDK). + */ + _logOutWith: function(provider) { + if (!this.isCurrent()) { + return; + } + if (_.isString(provider)) { + provider = Parse.User._authProviders[provider]; + } + if (provider && provider.deauthenticate) { + provider.deauthenticate(); + } + }, + + /** + * Signs up a new user. You should call this instead of save for + * new Parse.Users. This will create a new Parse.User on the server, and + * also persist the session on disk so that you can access the user using + * current. + * + *

A username and password must be set before calling signUp.

+ * + *

Calls options.success or options.error on completion.

+ * + * @param {Object} attrs Extra fields to set on the new user, or null. + * @param {Object} options A Backbone-style options object. + * @return {Parse.Promise} A promise that is fulfilled when the signup + * finishes. + * @see Parse.User.signUp + */ + signUp: function(attrs, options) { + var error; + options = options || {}; + + var username = (attrs && attrs.username) || this.get("username"); + if (!username || (username === "")) { + error = new Parse.Error( + Parse.Error.OTHER_CAUSE, + "Cannot sign up user with an empty name."); + if (options && options.error) { + options.error(this, error); + } + return Parse.Promise.error(error); + } + + var password = (attrs && attrs.password) || this.get("password"); + if (!password || (password === "")) { + error = new Parse.Error( + Parse.Error.OTHER_CAUSE, + "Cannot sign up user with an empty password."); + if (options && options.error) { + options.error(this, error); + } + return Parse.Promise.error(error); + } + + // Overridden so that the user can be made the current user. + var newOptions = _.clone(options); + newOptions.success = function(model) { + model._handleSaveResult(Parse.User._canUseCurrentUser()); + if (options.success) { + options.success.apply(this, arguments); + } + }; + return this.save(attrs, newOptions); + }, + + /** + * Logs in a Parse.User. On success, this saves the session to localStorage, + * so you can retrieve the currently logged in user using + * current. + * + *

A username and password must be set before calling logIn.

+ * + *

Calls options.success or options.error on completion.

+ * + * @param {Object} options A Backbone-style options object. + * @see Parse.User.logIn + * @return {Parse.Promise} A promise that is fulfilled with the user when + * the login is complete. + */ + logIn: function(options) { + if (!Parse.User._canUseCurrentUser()) { + throw new Error( + 'It is not possible to log in on a server environment.' + ); + } + var model = this; + options = options || {}; + var request = Parse._request({ + route: "login", + method: "GET", + useMasterKey: options.useMasterKey, + data: this.toJSON() + }); + return request.then(function(resp, status, xhr) { + var serverAttrs = model.parse(resp, status, xhr); + model._finishFetch(serverAttrs); + model._handleSaveResult(true); + return model; + })._thenRunCallbacks(options, this); + }, + + /** + * @see Parse.Object#save + */ + save: function(arg1, arg2, arg3) { + var i, attrs, current, options, saved; + if (_.isObject(arg1) || _.isNull(arg1) || _.isUndefined(arg1)) { + attrs = arg1; + options = arg2; + } else { + attrs = {}; + attrs[arg1] = arg2; + options = arg3; + } + options = options || {}; + + var newOptions = _.clone(options); + newOptions.success = function(model) { + model._handleSaveResult(false); + if (options.success) { + options.success.apply(this, arguments); + } + }; + return Parse.Object.prototype.save.call(this, attrs, newOptions); + }, + + /** + * @see Parse.Object#fetch + */ + fetch: function(options) { + var newOptions = options ? _.clone(options) : {}; + newOptions.success = function(model) { + model._handleSaveResult(false); + if (options && options.success) { + options.success.apply(this, arguments); + } + }; + return Parse.Object.prototype.fetch.call(this, newOptions); + }, + + /** + * Returns true if current would return this user. + * @see Parse.User#current + */ + isCurrent: function() { + return this._isCurrentUser; + }, + + /** + * Returns get("username"). + * @return {String} + * @see Parse.Object#get + */ + getUsername: function() { + return this.get("username"); + }, + + /** + * Calls set("username", username, options) and returns the result. + * @param {String} username + * @param {Object} options A Backbone-style options object. + * @return {Boolean} + * @see Parse.Object.set + */ + setUsername: function(username, options) { + return this.set("username", username, options); + }, + + /** + * Calls set("password", password, options) and returns the result. + * @param {String} password + * @param {Object} options A Backbone-style options object. + * @return {Boolean} + * @see Parse.Object.set + */ + setPassword: function(password, options) { + return this.set("password", password, options); + }, + + /** + * Returns get("email"). + * @return {String} + * @see Parse.Object#get + */ + getEmail: function() { + return this.get("email"); + }, + + /** + * Calls set("email", email, options) and returns the result. + * @param {String} email + * @param {Object} options A Backbone-style options object. + * @return {Boolean} + * @see Parse.Object.set + */ + setEmail: function(email, options) { + return this.set("email", email, options); + }, + + /** + * Checks whether this user is the current user and has been authenticated. + * @return (Boolean) whether this user is the current user and is logged in. + */ + authenticated: function() { + return !!this._sessionToken && + (Parse.User.current() && Parse.User.current().id === this.id); + }, + + /** + * Returns the session token for this user, if the user has been logged in, + * or if it is the result of a query with the master key. Otherwise, returns + * undefined. + * @return {String} the session token, or undefined + */ + getSessionToken: function() { + return this._sessionToken; + }, + + /** + * Request a revocable session token to replace the older style of token. + * @param {Object} options A Backbone-style options object. + * + * @return {Parse.Promise} A promise that is resolved when the replacement + * token has been fetched. + */ + _upgradeToRevocableSession: function(options) { + options = options || {}; + if (!Parse.User.current()) { + return Parse.Promise.as()._thenRunCallbacks(options); + } + var currentSession = Parse.User.current().getSessionToken(); + if (Parse.Session._isRevocable(currentSession)) { + return Parse.Promise.as()._thenRunCallbacks(options); + } + return Parse._request({ + route: 'upgradeToRevocableSession', + method: 'POST', + useMasterKey: options.useMasterKey, + sessionToken: currentSession + }).then(function(result) { + var session = new Parse.Session(); + session._finishFetch(result); + var currentUser = Parse.User.current(); + currentUser._sessionToken = session.getSessionToken(); + Parse.User._saveCurrentUser(currentUser); + })._thenRunCallbacks(options); + }, + + }, /** @lends Parse.User */ { + // Class Variables + + // The currently logged-in user. + _currentUser: null, + + // Whether currentUser is known to match the serialized version on disk. + // This is useful for saving a localstorage check if you try to load + // _currentUser frequently while there is none stored. + _currentUserMatchesDisk: false, + + // The localStorage key suffix that the current user is stored under. + _CURRENT_USER_KEY: "currentUser", + + // The mapping of auth provider names to actual providers + _authProviders: {}, + + // Whether to rewrite className User to _User + _performUserRewrite: true, + + // Whether to send a Revocable Session header + _isRevocableSessionEnabled: false, + + // Whether to enable a memory-unsafe current user in node.js + _enableUnsafeCurrentUser: false, + + + // Class Methods + + /** + * Signs up a new user with a username (or email) and password. + * This will create a new Parse.User on the server, and also persist the + * session in localStorage so that you can access the user using + * {@link #current}. + * + *

Calls options.success or options.error on completion.

+ * + * @param {String} username The username (or email) to sign up with. + * @param {String} password The password to sign up with. + * @param {Object} attrs Extra fields to set on the new user. + * @param {Object} options A Backbone-style options object. + * @return {Parse.Promise} A promise that is fulfilled with the user when + * the signup completes. + * @see Parse.User#signUp + */ + signUp: function(username, password, attrs, options) { + attrs = attrs || {}; + attrs.username = username; + attrs.password = password; + var user = Parse.Object._create("_User"); + return user.signUp(attrs, options); + }, + + /** + * Logs in a user with a username (or email) and password. On success, this + * saves the session to disk, so you can retrieve the currently logged in + * user using current. + * + *

Calls options.success or options.error on completion.

+ * + * @param {String} username The username (or email) to log in with. + * @param {String} password The password to log in with. + * @param {Object} options A Backbone-style options object. + * @return {Parse.Promise} A promise that is fulfilled with the user when + * the login completes. + * @see Parse.User#logIn + */ + logIn: function(username, password, options) { + var user = Parse.Object._create("_User"); + user._finishFetch({ username: username, password: password }); + return user.logIn(options); + }, + + /** + * Logs in a user with a session token. On success, this saves the session + * to disk, so you can retrieve the currently logged in user using + * current. + * + *

Calls options.success or options.error on completion.

+ * + * @param {String} sessionToken The sessionToken to log in with. + * @param {Object} options A Backbone-style options object. + * @return {Parse.Promise} A promise that is fulfilled with the user when + * the login completes. + */ + become: function(sessionToken, options) { + if (!Parse.User._canUseCurrentUser()) { + throw new Error( + 'It is not secure to become a user on a node.js server environment.' + ); + } + options = options || {}; + + var user = Parse.Object._create("_User"); + return Parse._request({ + route: "users", + className: "me", + method: "GET", + useMasterKey: options.useMasterKey, + sessionToken: sessionToken + }).then(function(resp, status, xhr) { + var serverAttrs = user.parse(resp, status, xhr); + user._finishFetch(serverAttrs); + user._handleSaveResult(true); + return user; + + })._thenRunCallbacks(options, user); + }, + + /** + * Logs out the currently logged in user session. This will remove the + * session from disk, log out of linked services, and future calls to + * current will return null. + * @return {Parse.Promise} A promise that is resolved when the session is + * destroyed on the server. + */ + logOut: function() { + if (!Parse.User._canUseCurrentUser()) { + throw new Error( + 'There is no current user user on a node.js server environment.' + ); + } + return Parse.User._currentAsync().then(function(currentUser) { + var promise = Parse.Storage.removeItemAsync( + Parse._getParsePath(Parse.User._CURRENT_USER_KEY)); + + if (currentUser !== null) { + var currentSession = currentUser.getSessionToken(); + if (Parse.Session._isRevocable(currentSession)) { + promise.then(function() { + return Parse._request({ + route: 'logout', + method: 'POST', + sessionToken: currentSession + }); + }); + } + currentUser._logOutWithAll(); + currentUser._isCurrentUser = false; + } + Parse.User._currentUserMatchesDisk = true; + Parse.User._currentUser = null; + + return promise; + }); + }, + + /** + * Requests a password reset email to be sent to the specified email address + * associated with the user account. This email allows the user to securely + * reset their password on the Parse site. + * + *

Calls options.success or options.error on completion.

+ * + * @param {String} email The email address associated with the user that + * forgot their password. + * @param {Object} options A Backbone-style options object. + */ + requestPasswordReset: function(email, options) { + options = options || {}; + var request = Parse._request({ + route: "requestPasswordReset", + method: "POST", + useMasterKey: options.useMasterKey, + data: { email: email } + }); + return request._thenRunCallbacks(options); + }, + + /** + * Retrieves the currently logged in ParseUser with a valid session, + * either from memory or localStorage, if necessary. + * @return {Parse.Object} The currently logged in Parse.User. + */ + current: function() { + if (!Parse.User._canUseCurrentUser()) { + throw new Error( + 'There is no current user user on a node.js server environment.' + ); + } + if (Parse.Storage.async) { + // We can't return the current user synchronously + Parse.User._currentAsync(); + return Parse.User._currentUser; + } + + if (Parse.User._currentUser) { + return Parse.User._currentUser; + } + + if (Parse.User._currentUserMatchesDisk) { + + return Parse.User._currentUser; + } + + // Load the user from local storage. + Parse.User._currentUserMatchesDisk = true; + + var userData = Parse.Storage.getItem(Parse._getParsePath( + Parse.User._CURRENT_USER_KEY)); + if (!userData) { + + return null; + } + Parse.User._currentUser = Parse.Object._create("_User"); + Parse.User._currentUser._isCurrentUser = true; + + var json = JSON.parse(userData); + Parse.User._currentUser.id = json._id; + delete json._id; + Parse.User._currentUser._sessionToken = json._sessionToken; + delete json._sessionToken; + Parse.User._currentUser._finishFetch(json); + + Parse.User._currentUser._synchronizeAllAuthData(); + Parse.User._currentUser._refreshCache(); + Parse.User._currentUser._opSetQueue = [{}]; + return Parse.User._currentUser; + }, + + /** + * Retrieves the currently logged in ParseUser from asynchronous Storage. + * @return {Parse.Promise} A Promise that is resolved with the currently + * logged in Parse User + */ + _currentAsync: function() { + if (Parse.User._currentUser) { + return Parse.Promise.as(Parse.User._currentUser); + } + + if (Parse.User._currentUserMatchesDisk) { + return Parse.Promise.as(Parse.User._currentUser); + } + + // Load the user from Storage + return Parse.Storage.getItemAsync(Parse._getParsePath( + Parse.User._CURRENT_USER_KEY)).then(function(userData) { + if (!userData) { + return null; + } + Parse.User._currentUser = Parse.Object._create("_User"); + Parse.User._currentUser._isCurrentUser = true; + + var json = JSON.parse(userData); + Parse.User._currentUser.id = json._id; + delete json._id; + Parse.User._currentUser._sessionToken = json._sessionToken; + delete json._sessionToken; + Parse.User._currentUser._finishFetch(json); + + Parse.User._currentUser._synchronizeAllAuthData(); + Parse.User._currentUser._refreshCache(); + Parse.User._currentUser._opSetQueue = [{}]; + return Parse.User._currentUser; + }); + }, + + /** + * Allow someone to define a custom User class without className + * being rewritten to _User. The default behavior is to rewrite + * User to _User for legacy reasons. This allows developers to + * override that behavior. + * + * @param {Boolean} isAllowed Whether or not to allow custom User class + */ + allowCustomUserClass: function(isAllowed) { + this._performUserRewrite = !isAllowed; + }, + + /** + * Allow a legacy application to start using revocable sessions. If the + * current session token is not revocable, a request will be made for a new, + * revocable session. + * It is not necessary to call this method from cloud code unless you are + * handling user signup or login from the server side. In a cloud code call, + * this function will not attempt to upgrade the current token. + * @param {Object} options A Backbone-style options object. + * + * @return {Parse.Promise} A promise that is resolved when the process has + * completed. If a replacement session token is requested, the promise + * will be resolved after a new token has been fetched. + */ + enableRevocableSession: function(options) { + options = options || {}; + Parse.User._isRevocableSessionEnabled = true; + if (Parse.User._canUseCurrentUser() && Parse.User.current()) { + return Parse.User.current()._upgradeToRevocableSession(options); + } + return Parse.Promise.as()._thenRunCallbacks(options); + }, + + /** + * + */ + enableUnsafeCurrentUser: function() { + Parse.User._enableUnsafeCurrentUser = true; + }, + + _canUseCurrentUser: function() { + return !Parse._isNode || Parse.User._enableUnsafeCurrentUser; + }, + + /** + * Persists a user as currentUser to localStorage, and into the singleton. + */ + _saveCurrentUser: function(user) { + if (Parse.User._currentUser !== null && + Parse.User._currentUser !== user) { + Parse.User.logOut(); + } + user._isCurrentUser = true; + Parse.User._currentUser = user; + Parse.User._currentUserMatchesDisk = true; + + var json = user.toJSON(); + json._id = user.id; + json._sessionToken = user._sessionToken; + if (Parse.Storage.async) { + Parse.Storage.setItemAsync( + Parse._getParsePath(Parse.User._CURRENT_USER_KEY), + JSON.stringify(json)); + } else { + Parse.Storage.setItem( + Parse._getParsePath(Parse.User._CURRENT_USER_KEY), + JSON.stringify(json)); + } + }, + + _registerAuthenticationProvider: function(provider) { + Parse.User._authProviders[provider.getAuthType()] = provider; + // Synchronize the current user with the auth provider. + if (Parse.User.current()) { + Parse.User.current()._synchronizeAuthData(provider.getAuthType()); + } + }, + + _logInWith: function(provider, options) { + var user = Parse.Object._create("_User"); + return user._linkWith(provider, options); + } + + }); +}(this)); + + +(function(root) { + root.Parse = root.Parse || {}; + var Parse = root.Parse; + + /** + * @class + * + *

A Parse.Session object is a local representation of a revocable session. + * This class is a subclass of a Parse.Object, and retains the same + * functionality of a Parse.Object.

+ */ + Parse.Session = Parse.Object.extend('_Session', + /** @lends Parse.Session.prototype */ + { + /** + * Returns the session token string. + * @return {String} + */ + getSessionToken: function() { + return this._sessionToken; + }, + + /** + * Internal method to handle special fields in a _Session response. + */ + _mergeMagicFields: function(attrs) { + if (attrs.sessionToken) { + this._sessionToken = attrs.sessionToken; + delete attrs.sessionToken; + } + Parse.Session.__super__._mergeMagicFields.call(this, attrs); + }, + }, /** @lends Parse.Session */ { + + // Throw an error when modifying these read-only fields + readOnlyAttributes: { + createdWith: true, + expiresAt: true, + installationId: true, + restricted: true, + sessionToken: true, + user: true + }, + + /** + * Retrieves the Session object for the currently logged in session. + * @return {Parse.Promise} A promise that is resolved with the Parse.Session + * object after it has been fetched. + */ + current: function(options) { + options = options || {}; + + var session = Parse.Object._create('_Session'); + var currentToken = Parse.User.current().getSessionToken(); + return Parse._request({ + route: 'sessions', + className: 'me', + method: 'GET', + useMasterKey: options.useMasterKey, + sessionToken: currentToken + }).then(function(resp, status, xhr) { + var serverAttrs = session.parse(resp, status, xhr); + session._finishFetch(serverAttrs); + return session; + })._thenRunCallbacks(options, session); + }, + + /** + * Determines whether a session token is revocable. + * @return {Boolean} + */ + _isRevocable: function(token) { + return token.indexOf('r:') > -1; + }, + + /** + * Determines whether the current session token is revocable. + * This method is useful for migrating Express.js or Node.js web apps to + * use revocable sessions. If you are migrating an app that uses the Parse + * SDK in the browser only, please use Parse.User.enableRevocableSession() + * instead, so that sessions can be automatically upgraded. + * @return {Boolean} + */ + isCurrentSessionRevocable: function() { + if (Parse.User.current() !== null) { + return Parse.Session._isRevocable( + Parse.User.current().getSessionToken() + ); + } + } + }); +})(this); + +// Parse.Query is a way to create a list of Parse.Objects. +(function(root) { + root.Parse = root.Parse || {}; + var Parse = root.Parse; + var _ = Parse._; + + /** + * Creates a new parse Parse.Query for the given Parse.Object subclass. + * @param objectClass - + * An instance of a subclass of Parse.Object, or a Parse className string. + * @class + * + *

Parse.Query defines a query that is used to fetch Parse.Objects. The + * most common use case is finding all objects that match a query through the + * find method. For example, this sample code fetches all objects + * of class MyClass. It calls a different function depending on + * whether the fetch succeeded or not. + * + *

+   * var query = new Parse.Query(MyClass);
+   * query.find({
+   *   success: function(results) {
+   *     // results is an array of Parse.Object.
+   *   },
+   *
+   *   error: function(error) {
+   *     // error is an instance of Parse.Error.
+   *   }
+   * });

+ * + *

A Parse.Query can also be used to retrieve a single object whose id is + * known, through the get method. For example, this sample code fetches an + * object of class MyClass and id myId. It calls a + * different function depending on whether the fetch succeeded or not. + * + *

+   * var query = new Parse.Query(MyClass);
+   * query.get(myId, {
+   *   success: function(object) {
+   *     // object is an instance of Parse.Object.
+   *   },
+   *
+   *   error: function(object, error) {
+   *     // error is an instance of Parse.Error.
+   *   }
+   * });

+ * + *

A Parse.Query can also be used to count the number of objects that match + * the query without retrieving all of those objects. For example, this + * sample code counts the number of objects of the class MyClass + *

+   * var query = new Parse.Query(MyClass);
+   * query.count({
+   *   success: function(number) {
+   *     // There are number instances of MyClass.
+   *   },
+   *
+   *   error: function(error) {
+   *     // error is an instance of Parse.Error.
+   *   }
+   * });

+ */ + Parse.Query = function(objectClass) { + if (_.isString(objectClass)) { + objectClass = Parse.Object._getSubclass(objectClass); + } + + this.objectClass = objectClass; + + this.className = objectClass.prototype.className; + + this._where = {}; + this._include = []; + this._limit = -1; // negative limit means, do not send a limit + this._skip = 0; + this._extraOptions = {}; + }; + + /** + * Constructs a Parse.Query that is the OR of the passed in queries. For + * example: + *
var compoundQuery = Parse.Query.or(query1, query2, query3);
+ * + * will create a compoundQuery that is an or of the query1, query2, and + * query3. + * @param {...Parse.Query} var_args The list of queries to OR. + * @return {Parse.Query} The query that is the OR of the passed in queries. + */ + Parse.Query.or = function() { + var queries = _.toArray(arguments); + var className = null; + Parse._arrayEach(queries, function(q) { + if (_.isNull(className)) { + className = q.className; + } + + if (className !== q.className) { + throw "All queries must be for the same class"; + } + }); + var query = new Parse.Query(className); + query._orQuery(queries); + return query; + }; + + Parse.Query.prototype = { + /** + * Constructs a Parse.Object whose id is already known by fetching data from + * the server. Either options.success or options.error is called when the + * find completes. + * + * @param {String} objectId The id of the object to be fetched. + * @param {Object} options A Backbone-style options object. + * Valid options are:
    + *
  • success: A Backbone-style success callback + *
  • error: An Backbone-style error callback. + *
  • useMasterKey: In Cloud Code and Node only, causes the Master Key to + * be used for this request. + *
  • sessionToken: A valid session token, used for making a request on + * behalf of a specific user. + *
+ */ + get: function(objectId, options) { + var self = this; + self.equalTo('objectId', objectId); + + var firstOptions = {}; + if (options && _.has(options, 'useMasterKey')) { + firstOptions = { useMasterKey: options.useMasterKey }; + } + if (options && _.has(options, 'sessionToken')) { + firstOptions.sessionToken = options.sessionToken; + } + + return self.first(firstOptions).then(function(response) { + if (response) { + return response; + } + + var errorObject = new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, + "Object not found."); + return Parse.Promise.error(errorObject); + + })._thenRunCallbacks(options, null); + }, + + /** + * Returns a JSON representation of this query. + * @return {Object} The JSON representation of the query. + */ + toJSON: function() { + var params = { + where: this._where + }; + + if (this._include.length > 0) { + params.include = this._include.join(","); + } + if (this._select) { + params.keys = this._select.join(","); + } + if (this._limit >= 0) { + params.limit = this._limit; + } + if (this._skip > 0) { + params.skip = this._skip; + } + if (this._order !== undefined) { + params.order = this._order.join(","); + } + + Parse._objectEach(this._extraOptions, function(v, k) { + params[k] = v; + }); + + return params; + }, + + /** + * Retrieves a list of ParseObjects that satisfy this query. + * Either options.success or options.error is called when the find + * completes. + * + * @param {Object} options A Backbone-style options object. Valid options + * are:
    + *
  • success: Function to call when the find completes successfully. + *
  • error: Function to call when the find fails. + *
  • useMasterKey: In Cloud Code and Node only, causes the Master Key to + * be used for this request. + *
  • sessionToken: A valid session token, used for making a request on + * behalf of a specific user. + *
+ * + * @return {Parse.Promise} A promise that is resolved with the results when + * the query completes. + */ + find: function(options) { + var self = this; + options = options || {}; + + var request = Parse._request({ + route: "classes", + className: this.className, + method: "GET", + useMasterKey: options.useMasterKey, + sessionToken: options.sessionToken, + data: this.toJSON() + }); + + return request.then(function(response) { + return _.map(response.results, function(json) { + var obj; + if (response.className) { + obj = new Parse.Object(response.className); + } else { + obj = new self.objectClass(); + } + obj._finishFetch(json, true); + return obj; + }); + })._thenRunCallbacks(options); + }, + + /** + * Counts the number of objects that match this query. + * Either options.success or options.error is called when the count + * completes. + * + * @param {Object} options A Backbone-style options object. Valid options + * are:
    + *
  • success: Function to call when the count completes successfully. + *
  • error: Function to call when the find fails. + *
  • useMasterKey: In Cloud Code and Node only, causes the Master Key to + * be used for this request. + *
  • sessionToken: A valid session token, used for making a request on + * behalf of a specific user. + *
+ * + * @return {Parse.Promise} A promise that is resolved with the count when + * the query completes. + */ + count: function(options) { + var self = this; + options = options || {}; + + var params = this.toJSON(); + params.limit = 0; + params.count = 1; + var request = Parse._request({ + route: "classes", + className: self.className, + method: "GET", + useMasterKey: options.useMasterKey, + sessionToken: options.sessionToken, + data: params + }); + + return request.then(function(response) { + return response.count; + })._thenRunCallbacks(options); + }, + + /** + * Retrieves at most one Parse.Object that satisfies this query. + * + * Either options.success or options.error is called when it completes. + * success is passed the object if there is one. otherwise, undefined. + * + * @param {Object} options A Backbone-style options object. Valid options + * are:
    + *
  • success: Function to call when the find completes successfully. + *
  • error: Function to call when the find fails. + *
  • useMasterKey: In Cloud Code and Node only, causes the Master Key to + * be used for this request. + *
  • sessionToken: A valid session token, used for making a request on + * behalf of a specific user. + *
+ * + * @return {Parse.Promise} A promise that is resolved with the object when + * the query completes. + */ + first: function(options) { + var self = this; + options = options || {}; + + var params = this.toJSON(); + params.limit = 1; + var request = Parse._request({ + route: "classes", + className: this.className, + method: "GET", + useMasterKey: options.useMasterKey, + sessionToken: options.sessionToken, + data: params + }); + + return request.then(function(response) { + return _.map(response.results, function(json) { + var obj; + if (response.className) { + obj = new Parse.Object(response.className); + } else { + obj = new self.objectClass(); + } + obj._finishFetch(json, true); + return obj; + })[0]; + })._thenRunCallbacks(options); + }, + + /** + * Returns a new instance of Parse.Collection backed by this query. + * @param {Array} items An array of instances of Parse.Object + * with which to start this Collection. + * @param {Object} options An optional object with Backbone-style options. + * Valid options are:
    + *
  • model: The Parse.Object subclass that this collection contains. + *
  • query: An instance of Parse.Query to use when fetching items. + *
  • comparator: A string property name or function to sort by. + *
+ * @return {Parse.Collection} + */ + collection: function(items, options) { + options = options || {}; + return new Parse.Collection(items, _.extend(options, { + model: this.objectClass, + query: this + })); + }, + + /** + * Sets the number of results to skip before returning any results. + * This is useful for pagination. + * Default is to skip zero results. + * @param {Number} n the number of results to skip. + * @return {Parse.Query} Returns the query, so you can chain this call. + */ + skip: function(n) { + this._skip = n; + return this; + }, + + /** + * Sets the limit of the number of results to return. The default limit is + * 100, with a maximum of 1000 results being returned at a time. + * @param {Number} n the number of results to limit to. + * @return {Parse.Query} Returns the query, so you can chain this call. + */ + limit: function(n) { + this._limit = n; + return this; + }, + + /** + * Add a constraint to the query that requires a particular key's value to + * be equal to the provided value. + * @param {String} key The key to check. + * @param value The value that the Parse.Object must contain. + * @return {Parse.Query} Returns the query, so you can chain this call. + */ + equalTo: function(key, value) { + if (_.isUndefined(value)) { + return this.doesNotExist(key); + } + + this._where[key] = Parse._encode(value); + return this; + }, + + /** + * Helper for condition queries + */ + _addCondition: function(key, condition, value) { + // Check if we already have a condition + if (!this._where[key]) { + this._where[key] = {}; + } + this._where[key][condition] = Parse._encode(value); + return this; + }, + + /** + * Add a constraint to the query that requires a particular key's value to + * be not equal to the provided value. + * @param {String} key The key to check. + * @param value The value that must not be equalled. + * @return {Parse.Query} Returns the query, so you can chain this call. + */ + notEqualTo: function(key, value) { + this._addCondition(key, "$ne", value); + return this; + }, + + /** + * Add a constraint to the query that requires a particular key's value to + * be less than the provided value. + * @param {String} key The key to check. + * @param value The value that provides an upper bound. + * @return {Parse.Query} Returns the query, so you can chain this call. + */ + lessThan: function(key, value) { + this._addCondition(key, "$lt", value); + return this; + }, + + /** + * Add a constraint to the query that requires a particular key's value to + * be greater than the provided value. + * @param {String} key The key to check. + * @param value The value that provides an lower bound. + * @return {Parse.Query} Returns the query, so you can chain this call. + */ + greaterThan: function(key, value) { + this._addCondition(key, "$gt", value); + return this; + }, + + /** + * Add a constraint to the query that requires a particular key's value to + * be less than or equal to the provided value. + * @param {String} key The key to check. + * @param value The value that provides an upper bound. + * @return {Parse.Query} Returns the query, so you can chain this call. + */ + lessThanOrEqualTo: function(key, value) { + this._addCondition(key, "$lte", value); + return this; + }, + + /** + * Add a constraint to the query that requires a particular key's value to + * be greater than or equal to the provided value. + * @param {String} key The key to check. + * @param value The value that provides an lower bound. + * @return {Parse.Query} Returns the query, so you can chain this call. + */ + greaterThanOrEqualTo: function(key, value) { + this._addCondition(key, "$gte", value); + return this; + }, + + /** + * Add a constraint to the query that requires a particular key's value to + * be contained in the provided list of values. + * @param {String} key The key to check. + * @param {Array} values The values that will match. + * @return {Parse.Query} Returns the query, so you can chain this call. + */ + containedIn: function(key, values) { + this._addCondition(key, "$in", values); + return this; + }, + + /** + * Add a constraint to the query that requires a particular key's value to + * not be contained in the provided list of values. + * @param {String} key The key to check. + * @param {Array} values The values that will not match. + * @return {Parse.Query} Returns the query, so you can chain this call. + */ + notContainedIn: function(key, values) { + this._addCondition(key, "$nin", values); + return this; + }, + + /** + * Add a constraint to the query that requires a particular key's value to + * contain each one of the provided list of values. + * @param {String} key The key to check. This key's value must be an array. + * @param {Array} values The values that will match. + * @return {Parse.Query} Returns the query, so you can chain this call. + */ + containsAll: function(key, values) { + this._addCondition(key, "$all", values); + return this; + }, + + + /** + * Add a constraint for finding objects that contain the given key. + * @param {String} key The key that should exist. + * @return {Parse.Query} Returns the query, so you can chain this call. + */ + exists: function(key) { + this._addCondition(key, "$exists", true); + return this; + }, + + /** + * Add a constraint for finding objects that do not contain a given key. + * @param {String} key The key that should not exist + * @return {Parse.Query} Returns the query, so you can chain this call. + */ + doesNotExist: function(key) { + this._addCondition(key, "$exists", false); + return this; + }, + + /** + * Add a regular expression constraint for finding string values that match + * the provided regular expression. + * This may be slow for large datasets. + * @param {String} key The key that the string to match is stored in. + * @param {RegExp} regex The regular expression pattern to match. + * @return {Parse.Query} Returns the query, so you can chain this call. + */ + matches: function(key, regex, modifiers) { + this._addCondition(key, "$regex", regex); + if (!modifiers) { modifiers = ""; } + // Javascript regex options support mig as inline options but store them + // as properties of the object. We support mi & should migrate them to + // modifiers + if (regex.ignoreCase) { modifiers += 'i'; } + if (regex.multiline) { modifiers += 'm'; } + + if (modifiers && modifiers.length) { + this._addCondition(key, "$options", modifiers); + } + return this; + }, + + /** + * Add a constraint that requires that a key's value matches a Parse.Query + * constraint. + * @param {String} key The key that the contains the object to match the + * query. + * @param {Parse.Query} query The query that should match. + * @return {Parse.Query} Returns the query, so you can chain this call. + */ + matchesQuery: function(key, query) { + var queryJSON = query.toJSON(); + queryJSON.className = query.className; + this._addCondition(key, "$inQuery", queryJSON); + return this; + }, + + /** + * Add a constraint that requires that a key's value not matches a + * Parse.Query constraint. + * @param {String} key The key that the contains the object to match the + * query. + * @param {Parse.Query} query The query that should not match. + * @return {Parse.Query} Returns the query, so you can chain this call. + */ + doesNotMatchQuery: function(key, query) { + var queryJSON = query.toJSON(); + queryJSON.className = query.className; + this._addCondition(key, "$notInQuery", queryJSON); + return this; + }, + + + /** + * Add a constraint that requires that a key's value matches a value in + * an object returned by a different Parse.Query. + * @param {String} key The key that contains the value that is being + * matched. + * @param {String} queryKey The key in the objects returned by the query to + * match against. + * @param {Parse.Query} query The query to run. + * @return {Parse.Query} Returns the query, so you can chain this call. + */ + matchesKeyInQuery: function(key, queryKey, query) { + var queryJSON = query.toJSON(); + queryJSON.className = query.className; + this._addCondition(key, "$select", + { key: queryKey, query: queryJSON }); + return this; + }, + + /** + * Add a constraint that requires that a key's value not match a value in + * an object returned by a different Parse.Query. + * @param {String} key The key that contains the value that is being + * excluded. + * @param {String} queryKey The key in the objects returned by the query to + * match against. + * @param {Parse.Query} query The query to run. + * @return {Parse.Query} Returns the query, so you can chain this call. + */ + doesNotMatchKeyInQuery: function(key, queryKey, query) { + var queryJSON = query.toJSON(); + queryJSON.className = query.className; + this._addCondition(key, "$dontSelect", + { key: queryKey, query: queryJSON }); + return this; + }, + + /** + * Add constraint that at least one of the passed in queries matches. + * @param {Array} queries + * @return {Parse.Query} Returns the query, so you can chain this call. + */ + _orQuery: function(queries) { + var queryJSON = _.map(queries, function(q) { + return q.toJSON().where; + }); + + this._where.$or = queryJSON; + return this; + }, + + /** + * Converts a string into a regex that matches it. + * Surrounding with \Q .. \E does this, we just need to escape \E's in + * the text separately. + */ + _quote: function(s) { + return "\\Q" + s.replace("\\E", "\\E\\\\E\\Q") + "\\E"; + }, + + /** + * Add a constraint for finding string values that contain a provided + * string. This may be slow for large datasets. + * @param {String} key The key that the string to match is stored in. + * @param {String} substring The substring that the value must contain. + * @return {Parse.Query} Returns the query, so you can chain this call. + */ + contains: function(key, value) { + this._addCondition(key, "$regex", this._quote(value)); + return this; + }, + + /** + * Add a constraint for finding string values that start with a provided + * string. This query will use the backend index, so it will be fast even + * for large datasets. + * @param {String} key The key that the string to match is stored in. + * @param {String} prefix The substring that the value must start with. + * @return {Parse.Query} Returns the query, so you can chain this call. + */ + startsWith: function(key, value) { + this._addCondition(key, "$regex", "^" + this._quote(value)); + return this; + }, + + /** + * Add a constraint for finding string values that end with a provided + * string. This will be slow for large datasets. + * @param {String} key The key that the string to match is stored in. + * @param {String} suffix The substring that the value must end with. + * @return {Parse.Query} Returns the query, so you can chain this call. + */ + endsWith: function(key, value) { + this._addCondition(key, "$regex", this._quote(value) + "$"); + return this; + }, + + /** + * Sorts the results in ascending order by the given key. + * + * @param {(String|String[]|...String} key The key to order by, which is a + * string of comma separated values, or an Array of keys, or multiple keys. + * @return {Parse.Query} Returns the query, so you can chain this call. + */ + ascending: function() { + this._order = []; + return this.addAscending.apply(this, arguments); + }, + + /** + * Sorts the results in ascending order by the given key, + * but can also add secondary sort descriptors without overwriting _order. + * + * @param {(String|String[]|...String} key The key to order by, which is a + * string of comma separated values, or an Array of keys, or multiple keys. + * @return {Parse.Query} Returns the query, so you can chain this call. + */ + addAscending: function(key) { + var self = this; + if (!this._order) { + this._order = []; + } + Parse._arrayEach(arguments, function(key) { + if (Array.isArray(key)) { + key = key.join(); + } + self._order = self._order.concat(key.replace(/\s/g, "").split(",")); + }); + return this; + }, + + /** + * Sorts the results in descending order by the given key. + * + * @param {(String|String[]|...String} key The key to order by, which is a + * string of comma separated values, or an Array of keys, or multiple keys. + * @return {Parse.Query} Returns the query, so you can chain this call. + */ + descending: function(key) { + this._order = []; + return this.addDescending.apply(this, arguments); + }, + + /** + * Sorts the results in descending order by the given key, + * but can also add secondary sort descriptors without overwriting _order. + * + * @param {(String|String[]|...String} key The key to order by, which is a + * string of comma separated values, or an Array of keys, or multiple keys. + * @return {Parse.Query} Returns the query, so you can chain this call. + */ + addDescending: function(key) { + var self = this; + if (!this._order) { + this._order = []; + } + Parse._arrayEach(arguments, function(key) { + if (Array.isArray(key)) { + key = key.join(); + } + self._order = self._order.concat( + _.map(key.replace(/\s/g, "").split(","), + function(k) { return "-" + k; })); + }); + return this; + }, + + /** + * Add a proximity based constraint for finding objects with key point + * values near the point given. + * @param {String} key The key that the Parse.GeoPoint is stored in. + * @param {Parse.GeoPoint} point The reference Parse.GeoPoint that is used. + * @return {Parse.Query} Returns the query, so you can chain this call. + */ + near: function(key, point) { + if (!(point instanceof Parse.GeoPoint)) { + // Try to cast it to a GeoPoint, so that near("loc", [20,30]) works. + point = new Parse.GeoPoint(point); + } + this._addCondition(key, "$nearSphere", point); + return this; + }, + + /** + * Add a proximity based constraint for finding objects with key point + * values near the point given and within the maximum distance given. + * @param {String} key The key that the Parse.GeoPoint is stored in. + * @param {Parse.GeoPoint} point The reference Parse.GeoPoint that is used. + * @param {Number} maxDistance Maximum distance (in radians) of results to + * return. + * @return {Parse.Query} Returns the query, so you can chain this call. + */ + withinRadians: function(key, point, distance) { + this.near(key, point); + this._addCondition(key, "$maxDistance", distance); + return this; + }, + + /** + * Add a proximity based constraint for finding objects with key point + * values near the point given and within the maximum distance given. + * Radius of earth used is 3958.8 miles. + * @param {String} key The key that the Parse.GeoPoint is stored in. + * @param {Parse.GeoPoint} point The reference Parse.GeoPoint that is used. + * @param {Number} maxDistance Maximum distance (in miles) of results to + * return. + * @return {Parse.Query} Returns the query, so you can chain this call. + */ + withinMiles: function(key, point, distance) { + return this.withinRadians(key, point, distance / 3958.8); + }, + + /** + * Add a proximity based constraint for finding objects with key point + * values near the point given and within the maximum distance given. + * Radius of earth used is 6371.0 kilometers. + * @param {String} key The key that the Parse.GeoPoint is stored in. + * @param {Parse.GeoPoint} point The reference Parse.GeoPoint that is used. + * @param {Number} maxDistance Maximum distance (in kilometers) of results + * to return. + * @return {Parse.Query} Returns the query, so you can chain this call. + */ + withinKilometers: function(key, point, distance) { + return this.withinRadians(key, point, distance / 6371.0); + }, + + /** + * Add a constraint to the query that requires a particular key's + * coordinates be contained within a given rectangular geographic bounding + * box. + * @param {String} key The key to be constrained. + * @param {Parse.GeoPoint} southwest + * The lower-left inclusive corner of the box. + * @param {Parse.GeoPoint} northeast + * The upper-right inclusive corner of the box. + * @return {Parse.Query} Returns the query, so you can chain this call. + */ + withinGeoBox: function(key, southwest, northeast) { + if (!(southwest instanceof Parse.GeoPoint)) { + southwest = new Parse.GeoPoint(southwest); + } + if (!(northeast instanceof Parse.GeoPoint)) { + northeast = new Parse.GeoPoint(northeast); + } + this._addCondition(key, '$within', { '$box': [southwest, northeast] }); + return this; + }, + + /** + * Include nested Parse.Objects for the provided key. You can use dot + * notation to specify which fields in the included object are also fetched. + * @param {String} key The name of the key to include. + * @return {Parse.Query} Returns the query, so you can chain this call. + */ + include: function() { + var self = this; + Parse._arrayEach(arguments, function(key) { + if (_.isArray(key)) { + self._include = self._include.concat(key); + } else { + self._include.push(key); + } + }); + return this; + }, + + /** + * Restrict the fields of the returned Parse.Objects to include only the + * provided keys. If this is called multiple times, then all of the keys + * specified in each of the calls will be included. + * @param {Array} keys The names of the keys to include. + * @return {Parse.Query} Returns the query, so you can chain this call. + */ + select: function() { + var self = this; + this._select = this._select || []; + Parse._arrayEach(arguments, function(key) { + if (_.isArray(key)) { + self._select = self._select.concat(key); + } else { + self._select.push(key); + } + }); + return this; + }, + + /** + * Iterates over each result of a query, calling a callback for each one. If + * the callback returns a promise, the iteration will not continue until + * that promise has been fulfilled. If the callback returns a rejected + * promise, then iteration will stop with that error. The items are + * processed in an unspecified order. The query may not have any sort order, + * and may not use limit or skip. + * @param {Function} callback Callback that will be called with each result + * of the query. + * @param {Object} options An optional Backbone-like options object with + * success and error callbacks that will be invoked once the iteration + * has finished. + * @return {Parse.Promise} A promise that will be fulfilled once the + * iteration has completed. + */ + each: function(callback, options) { + options = options || {}; + + if (this._order || this._skip || (this._limit >= 0)) { + var error = + "Cannot iterate on a query with sort, skip, or limit."; + return Parse.Promise.error(error)._thenRunCallbacks(options); + } + + var promise = new Parse.Promise(); + + var query = new Parse.Query(this.objectClass); + // We can override the batch size from the options. + // This is undocumented, but useful for testing. + query._limit = options.batchSize || 100; + query._where = _.clone(this._where); + query._include = _.clone(this._include); + if (this._select) { + query._select = _.clone(this._select); + } + + query.ascending('objectId'); + + var findOptions = {}; + if (_.has(options, "useMasterKey")) { + findOptions.useMasterKey = options.useMasterKey; + } + if (_.has(options, 'sessionToken')) { + findOptions.sessionToken = options.sessionToken; + } + + var finished = false; + return Parse.Promise._continueWhile(function() { + return !finished; + + }, function() { + return query.find(findOptions).then(function(results) { + var callbacksDone = Parse.Promise.as(); + Parse._.each(results, function(result) { + callbacksDone = callbacksDone.then(function() { + return callback(result); + }); + }); + + return callbacksDone.then(function() { + if (results.length >= query._limit) { + query.greaterThan("objectId", results[results.length - 1].id); + } else { + finished = true; + } + }); + }); + })._thenRunCallbacks(options); + } + }; + +}(this)); + +/*global FB: false , console: false*/ +(function(root) { + root.Parse = root.Parse || {}; + var Parse = root.Parse; + var _ = Parse._; + + var PUBLIC_KEY = "*"; + + var initialized = false; + var requestedPermissions; + var initOptions; + var provider = { + authenticate: function(options) { + var self = this; + FB.login(function(response) { + if (response.authResponse) { + if (options.success) { + options.success(self, { + id: response.authResponse.userID, + access_token: response.authResponse.accessToken, + expiration_date: new Date(response.authResponse.expiresIn * 1000 + + (new Date()).getTime()).toJSON() + }); + } + } else { + if (options.error) { + options.error(self, response); + } + } + }, { + scope: requestedPermissions + }); + }, + restoreAuthentication: function(authData) { + if (authData) { + var authResponse = { + userID: authData.id, + accessToken: authData.access_token, + expiresIn: (Parse._parseDate(authData.expiration_date).getTime() - + (new Date()).getTime()) / 1000 + }; + var newOptions = _.clone(initOptions); + newOptions.authResponse = authResponse; + + // Suppress checks for login status from the browser. + newOptions.status = false; + + // If the user doesn't match the one known by the FB SDK, log out. + // Most of the time, the users will match -- it's only in cases where + // the FB SDK knows of a different user than the one being restored + // from a Parse User that logged in with username/password. + var existingResponse = FB.getAuthResponse(); + if (existingResponse && + existingResponse.userID !== authResponse.userID) { + FB.logout(); + } + + FB.init(newOptions); + } + return true; + }, + getAuthType: function() { + return "facebook"; + }, + deauthenticate: function() { + this.restoreAuthentication(null); + } + }; + + /** + * Provides a set of utilities for using Parse with Facebook. + * @namespace + * Provides a set of utilities for using Parse with Facebook. + */ + Parse.FacebookUtils = { + /** + * Initializes Parse Facebook integration. Call this function after you + * have loaded the Facebook Javascript SDK with the same parameters + * as you would pass to + * + * FB.init(). Parse.FacebookUtils will invoke FB.init() for you + * with these arguments. + * + * @param {Object} options Facebook options argument as described here: + * + * FB.init(). The status flag will be coerced to 'false' because it + * interferes with Parse Facebook integration. Call FB.getLoginStatus() + * explicitly if this behavior is required by your application. + */ + init: function(options) { + if (typeof(FB) === 'undefined') { + throw "The Facebook JavaScript SDK must be loaded before calling init."; + } + initOptions = _.clone(options) || {}; + if (initOptions.status && typeof(console) !== "undefined") { + var warn = console.warn || console.log || function() {}; + warn.call(console, "The 'status' flag passed into" + + " FB.init, when set to true, can interfere with Parse Facebook" + + " integration, so it has been suppressed. Please call" + + " FB.getLoginStatus() explicitly if you require this behavior."); + } + initOptions.status = false; + FB.init(initOptions); + Parse.User._registerAuthenticationProvider(provider); + initialized = true; + }, + + /** + * Gets whether the user has their account linked to Facebook. + * + * @param {Parse.User} user User to check for a facebook link. + * The user must be logged in on this device. + * @return {Boolean} true if the user has their account + * linked to Facebook. + */ + isLinked: function(user) { + return user._isLinked("facebook"); + }, + + /** + * Logs in a user using Facebook. This method delegates to the Facebook + * SDK to authenticate the user, and then automatically logs in (or + * creates, in the case where it is a new user) a Parse.User. + * + * @param {String, Object} permissions The permissions required for Facebook + * log in. This is a comma-separated string of permissions. + * Alternatively, supply a Facebook authData object as described in our + * REST API docs if you want to handle getting facebook auth tokens + * yourself. + * @param {Object} options Standard options object with success and error + * callbacks. + */ + logIn: function(permissions, options) { + if (!permissions || _.isString(permissions)) { + if (!initialized) { + throw "You must initialize FacebookUtils before calling logIn."; + } + requestedPermissions = permissions; + return Parse.User._logInWith("facebook", options); + } else { + var newOptions = _.clone(options) || {}; + newOptions.authData = permissions; + return Parse.User._logInWith("facebook", newOptions); + } + }, + + /** + * Links Facebook to an existing PFUser. This method delegates to the + * Facebook SDK to authenticate the user, and then automatically links + * the account to the Parse.User. + * + * @param {Parse.User} user User to link to Facebook. This must be the + * current user. + * @param {String, Object} permissions The permissions required for Facebook + * log in. This is a comma-separated string of permissions. + * Alternatively, supply a Facebook authData object as described in our + * REST API docs if you want to handle getting facebook auth tokens + * yourself. + * @param {Object} options Standard options object with success and error + * callbacks. + */ + link: function(user, permissions, options) { + if (!permissions || _.isString(permissions)) { + if (!initialized) { + throw "You must initialize FacebookUtils before calling link."; + } + requestedPermissions = permissions; + return user._linkWith("facebook", options); + } else { + var newOptions = _.clone(options) || {}; + newOptions.authData = permissions; + return user._linkWith("facebook", newOptions); + } + }, + + /** + * Unlinks the Parse.User from a Facebook account. + * + * @param {Parse.User} user User to unlink from Facebook. This must be the + * current user. + * @param {Object} options Standard options object with success and error + * callbacks. + */ + unlink: function(user, options) { + if (!initialized) { + throw "You must initialize FacebookUtils before calling unlink."; + } + return user._unlinkFrom("facebook", options); + } + }; + +}(this)); + +/*global _: false, document: false, window: false, navigator: false */ +(function(root) { + root.Parse = root.Parse || {}; + var Parse = root.Parse; + var _ = Parse._; + + /** + * History serves as a global router (per frame) to handle hashchange + * events or pushState, match the appropriate route, and trigger + * callbacks. You shouldn't ever have to create one of these yourself + * — you should use the reference to Parse.history + * that will be created for you automatically if you make use of + * Routers with routes. + * @class + * + *

A fork of Backbone.History, provided for your convenience. If you + * use this class, you must also include jQuery, or another library + * that provides a jQuery-compatible $ function. For more information, + * see the + * Backbone documentation.

+ *

Available in the client SDK only.

+ */ + Parse.History = function() { + this.handlers = []; + _.bindAll(this, 'checkUrl'); + }; + + // Cached regex for cleaning leading hashes and slashes . + var routeStripper = /^[#\/]/; + + // Cached regex for detecting MSIE. + var isExplorer = /msie [\w.]+/; + + // Has the history handling already been started? + Parse.History.started = false; + + // Set up all inheritable **Parse.History** properties and methods. + _.extend(Parse.History.prototype, Parse.Events, + /** @lends Parse.History.prototype */ { + + // The default interval to poll for hash changes, if necessary, is + // twenty times a second. + interval: 50, + + // Gets the true hash value. Cannot use location.hash directly due to bug + // in Firefox where location.hash will always be decoded. + getHash: function(windowOverride) { + var loc = windowOverride ? windowOverride.location : window.location; + var match = loc.href.match(/#(.*)$/); + return match ? match[1] : ''; + }, + + // Get the cross-browser normalized URL fragment, either from the URL, + // the hash, or the override. + getFragment: function(fragment, forcePushState) { + if (Parse._isNullOrUndefined(fragment)) { + if (this._hasPushState || forcePushState) { + fragment = window.location.pathname; + var search = window.location.search; + if (search) { + fragment += search; + } + } else { + fragment = this.getHash(); + } + } + if (!fragment.indexOf(this.options.root)) { + fragment = fragment.substr(this.options.root.length); + } + return fragment.replace(routeStripper, ''); + }, + + /** + * Start the hash change handling, returning `true` if the current + * URL matches an existing route, and `false` otherwise. + */ + start: function(options) { + if (Parse.History.started) { + throw new Error("Parse.history has already been started"); + } + Parse.History.started = true; + + // Figure out the initial configuration. Do we need an iframe? + // Is pushState desired ... is it available? + this.options = _.extend({}, {root: '/'}, this.options, options); + this._wantsHashChange = this.options.hashChange !== false; + this._wantsPushState = !!this.options.pushState; + this._hasPushState = !!(this.options.pushState && + window.history && + window.history.pushState); + var fragment = this.getFragment(); + var docMode = document.documentMode; + var oldIE = (isExplorer.exec(navigator.userAgent.toLowerCase()) && + (!docMode || docMode <= 7)); + + if (oldIE) { + this.iframe = Parse.$(' -
スライドが見えない場合はコチラから閲覧してください
- +
+ +
スライドが見えない場合はコチラから閲覧してください
+

さらに詳しい道場の様子については、「CoderDojoとは? 運営者に話を聞いてみた! (バケモノ.jp)」や「子どものための無料プログラミング道場 CoderDojo をはじめてみませんか (ICT教育ニュース)」を読んでみてください。それぞれインタビュー形式で話が構成されているので、より具体的なイメージが掴めるかなと思います :)

- -
+

全国の道場

  • @@ -67,61 +42,62 @@
    道場情報を読み込み中...
-
+
+
+
+

CoderDojo をもっと知りたい

+

下記ウェブサイトから、より詳細な情報や、最新の情報にアクセスできます。

+ +
-
-

CoderDojo をもっと知りたい

-

下記ウェブサイトから、より詳細な情報や、最新の情報にアクセスできます。

- -
- -

本家サイトである CoderDojo (英語) では世界中の活動の様子が見れます。また、Facebook の CoderDojo Japan では日本全国の活動の様子を知ることができます。

-

「これまでの成り立ちを知りたい」「地元で CoderDojo を立ち上げてみたい」といった場合には CoderDojo Kata を参照してみてください。

-
-

お問い合わせ

-

取材や支援などのお問い合わせについては、下記フォームからお気軽にご連絡ください :)
-メールをご希望の場合は、担当の安川要平まで (yohei@coderdojo.jp) ご連絡ください。

+

本家サイトである CoderDojo (英語) では世界中の活動の様子が見れます。また、Facebook の CoderDojo Japan では日本全国の活動の様子を知ることができます。

+

「これまでの成り立ちを知りたい」「地元で CoderDojo を立ち上げてみたい」といった場合には CoderDojo Kata を参照してみてください。

+
+

お問い合わせ

+

取材や支援などのお問い合わせについては、下記フォームからお気軽にご連絡ください :)
+ メールをご希望の場合は、担当の安川要平まで (yohei@coderdojo.jp) ご連絡ください。

- -
- -
HTML Forms powered by Wufoo.
- -
+ +
+ +
HTML Forms powered by Wufoo.
+ +
+
-