Commit 1166da775fd8abe9297295e63d6454285336b50e

Authored by Evandro Junior
2 parents 7a5a8587 b0c86f25

Merge branch 'jasmine-testing'

Conflicts:
	js/main.js
index.html
... ... @@ -7,33 +7,58 @@
7 7 <meta name="viewport" content="width=device-width, initial-scale=1">
8 8  
9 9 <script type="text/javascript">
10   - function removeUrlParameters(full_url){
11   - var url_base = full_url.split('#').shift();
12   - return url_base;
13   - }
14   - function addBaseUrl(filename){
15   - var base = window.location.href;
16   - if(base.match(/file:/) || filename.match(/http:/)){
17   - base = '';
18   - }else{
19   - base = removeUrlParameters(base) + '/';
  10 + /**
  11 + * The Url created with module pattern
  12 + *
  13 + * @todo Moves the module to another single js file
  14 + */
  15 + var Url = (function(){
  16 +
  17 + return {
  18 +
  19 + initBase: function(){
  20 + var base = window.location.href;
  21 + var host = window.location.host;
  22 + var regex = new RegExp(".*" + host + '/', "g");
  23 +
  24 + if(host){
  25 + base = base.replace(regex,'');
  26 + }else{
  27 + base = base.replace('index.html','');
  28 + }
  29 + return base
  30 + },
  31 + removeUrlParameters: function(full_url){
  32 + var url_base = full_url.split('#').shift();
  33 + return url_base;
  34 + },
  35 + addBaseUrl: function(filename){
  36 + var base = window.location.href;
  37 + if(base.match(/file:/) || filename.match(/http:/)){
  38 + base = '';
  39 + }else{
  40 + base = this.removeUrlParameters(base) + '/';
  41 + }
  42 + return base + filename;
  43 + }
20 44 }
21   - return base + filename;
22   - }
  45 +
  46 + })();
  47 +
23 48 function makeCSSLink(filename){
24 49 var fileref=document.createElement('link');
25 50 fileref.setAttribute('rel', 'stylesheet');
26 51 fileref.setAttribute('type', 'text/css');
27   - fileref.setAttribute('href', addBaseUrl(filename));
  52 + fileref.setAttribute('href', Url.addBaseUrl(filename));
28 53 return fileref;
29 54 }
30 55 function loadCSSFiles(){
31 56 var css_files = [
32   - 'css/bootstrap.min.css',
33   - 'css/hover.custom.css',
34   - 'http://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css',
35   - 'http://fonts.googleapis.com/css?family=Open+Sans:400,300,700',
36   - 'http://fonts.googleapis.com/css?family=Asap:400,700',
  57 + 'css/bootstrap.min.css',
  58 + 'css/hover.custom.css',
  59 + 'http://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css',
  60 + 'http://fonts.googleapis.com/css?family=Open+Sans:400,300,700',
  61 + 'http://fonts.googleapis.com/css?family=Asap:400,700',
37 62 'style.css'
38 63 ];
39 64 head = document.getElementsByTagName('head')[0]
... ... @@ -42,8 +67,8 @@
42 67 }
43 68 }
44 69 function makeJSURL(filename){
45   - var url = addBaseUrl(filename);
46   -
  70 + var url = Url.addBaseUrl(filename);
  71 +
47 72 return url;
48 73 }
49 74  
... ... @@ -66,17 +91,17 @@
66 91  
67 92 <div id="proposal-result"></div>
68 93 <div class="clearfix"></div>
69   -
  94 +
70 95 <script id="proposal-template" type="text/x-handlebars-template">
71 96 <header>
72 97 <h1><a href="#">{{article.title}}</a></h1>
73 98 <a id="display-contrast" href="#">Alto Contraste</a>
74 99 </header>
75   -
  100 +
76 101 <div id="content">
77 102 {{{article.abstract}}}
78 103 </div>
79   -
  104 +
80 105 <nav role="tabpanel">
81 106 <ul role="tablist">
82 107 <li id="nav-proposal-categories" role="presentation">
... ... @@ -112,7 +137,7 @@
112 137 </div>
113 138 </div>
114 139  
115   - <div id="proposal-group" class="hide">
  140 + <div id="proposal-group" class="hide">
116 141 <label for="search-input">Selecione um programa para fazer propostas e contar sua experiência.</label>
117 142 <div id="search-input-container">
118 143 <input placeholder="palavra chave" id="search-input" type="text" name="search" />
... ... @@ -160,7 +185,7 @@
160 185 <span class="fa fa-reply"></span>
161 186 Agora contribua para a melhoria desse programa
162 187 </a>
163   - </div>
  188 + </div>
164 189 </div>
165 190  
166 191 <div class="make-proposal-container">
... ... @@ -303,7 +328,7 @@
303 328 </script>
304 329  
305 330 <script type='text/javascript' >
306   -loadRequireJS();
  331 + loadRequireJS();
307 332 </script>
308 333  
309 334  
... ...
js/main.js
1   -define(['handlebars'], function(Handlebars){
  1 +define(['handlebars','handlebars_helpers'], function(Handlebars){
  2 +
2 3  
3 4 /* global Handlebars, $ */
4 5 // The template code
5   - var templateSource = document.getElementById('proposal-template').innerHTML;
  6 + var templateSource = $('#proposal-template').html();
6 7  
7 8 // compile the template
8 9 var template = Handlebars.compile(templateSource);
... ... @@ -41,7 +42,7 @@ define([&#39;handlebars&#39;], function(Handlebars){
41 42 $('.login-container').html(loginTemplate());
42 43 $('.countdown').maxlength({text: '%left caracteres restantes'});
43 44  
44   - navigateTo(window.location.hash);
  45 + Main.navigateTo(window.location.hash);
45 46  
46 47 //Actions for links
47 48 $( '#nav-proposal-categories a' ).on('click touchstart', function(e){
... ... @@ -50,7 +51,7 @@ define([&#39;handlebars&#39;], function(Handlebars){
50 51 var $link = $(this);
51 52  
52 53 // Update URL and Navigate
53   - updateHash($link.attr('href'));
  54 + Main.updateHash($link.attr('href'));
54 55 });
55 56  
56 57 $( '#nav-proposal-group a' ).on('click touchstart', function(e){
... ... @@ -59,7 +60,7 @@ define([&#39;handlebars&#39;], function(Handlebars){
59 60 var $link = $(this);
60 61  
61 62 // Update URL and Navigate
62   - updateHash($link.attr('href'));
  63 + Main.updateHash($link.attr('href'));
63 64 });
64 65  
65 66 $( '.proposal-item a' ).on('click touchstart', function(e){
... ... @@ -68,7 +69,7 @@ define([&#39;handlebars&#39;], function(Handlebars){
68 69 var $link = $(this);
69 70  
70 71 // Update URL and Navigate
71   - updateHash($link.attr('href'));
  72 + Main.updateHash($link.attr('href'));
72 73 });
73 74  
74 75 $( '.proposal-category a' ).on('click touchstart', function(e){
... ... @@ -77,7 +78,7 @@ define([&#39;handlebars&#39;], function(Handlebars){
77 78 var $link = $(this);
78 79  
79 80 // Update URL and Navigate
80   - updateHash($link.attr('href'));
  81 + Main.updateHash($link.attr('href'));
81 82 });
82 83  
83 84 $( '.proposal-category .go-back' ).on('click touchstart', function(e){
... ... @@ -94,7 +95,7 @@ define([&#39;handlebars&#39;], function(Handlebars){
94 95 }
95 96  
96 97 // Update URL and Navigate
97   - updateHash(newHash);
  98 + Main.updateHash(newHash);
98 99 });
99 100  
100 101 $( '.send-button a' ).on('click touchstart', function(e){
... ... @@ -105,7 +106,7 @@ define([&#39;handlebars&#39;], function(Handlebars){
105 106 loginButton = $this.parents('.send-button');
106 107 loginButton.hide();
107 108 $this.parents('.success-proposal-sent').hide();
108   - loginCallback(logged_in);
  109 + Main.loginCallback(logged_in);
109 110 });
110 111  
111 112 $( '#display-contrast' ).on('click touchstart', function(e){
... ... @@ -119,7 +120,7 @@ define([&#39;handlebars&#39;], function(Handlebars){
119 120 var $link = $(this);
120 121  
121 122 // Update URL and Navigate
122   - updateHash($link.attr('href'));
  123 + Main.updateHash($link.attr('href'));
123 124 });
124 125  
125 126 $( '.go-to-proposal-button a' ).on('click touchstart', function(e){
... ... @@ -128,13 +129,13 @@ define([&#39;handlebars&#39;], function(Handlebars){
128 129 var $link = $(this);
129 130  
130 131 // Update URL and Navigate
131   - updateHash($link.attr('href'));
  132 + Main.updateHash($link.attr('href'));
132 133 });
133 134  
134 135 $( '.proposal-selection' ).change(function(e){
135 136 e.preventDefault();
136 137  
137   - display_proposal('proposal-item-' + this.value);
  138 + Main.display_proposal('proposal-item-' + this.value);
138 139 });
139 140  
140 141 var availableTags = [ ];
... ... @@ -189,96 +190,336 @@ define([&#39;handlebars&#39;], function(Handlebars){
189 190 console.log( 'Request Failed: ' + err );
190 191 });
191 192  
192   - function loadRandomProposal(topic_id, private_token) {
193   - var $noProposals = $('.no-proposals');
194   - var $loading = $('.loading');
195   - var $randomProposal = $('.random-proposal');
196   - var $body = $(document.body);
197   -
198   - // reset view
199   - $noProposals.hide();
200   - $loading.show();
201   - $randomProposal.html('');
202   -
203   - var url = host + '/api/v1/articles/' + topic_id + '/children' + '?private_token=' + private_token + '&limit=1&order=random()&_='+new Date().getTime()+'&fields=id,name,abstract,created_by&content_type=ProposalsDiscussionPlugin::Proposal';
204   - $.getJSON(url).done(function( data ) {
205   - $loading.hide();
206   -
207   - if(data.articles.length === 0) {
208   - $noProposals.show();
209   - return;
210   - }
211   -
212   - var article = data.articles[0];
213   - $randomProposal.html(supportProposalTemplate(article));
214   - $body.off('click', '.vote-actions .skip');
215   - $body.on('click', '.vote-actions .skip', function(e) {
216   - loadRandomProposal(topic_id, private_token);
217   - e.preventDefault();
218   - });
219   - $body.off('click', '.vote-actions .like');
220   - $body.on('click', '.vote-actions .like', function(e) {
221   - //Helps to prevent more than one vote per proposal
222   - if(hasProposalbeenVoted(article.id)){
223   - console.log("Proposta " + article.id + " já havia sido votada");
224   - loadRandomProposal(topic_id, private_token);
225   - e.preventDefault();
226   - return;
227   - }
228   - $.ajax({
229   - type: 'post',
230   - url: host + '/api/v1/articles/' + article.id + '/vote',
231   - data: {
232   - value: $(this).data('vote-value'),
233   - private_token: private_token
  193 + var BARRA_ADDED = false;
  194 + var HIDE_BARRA_DO_GOVERNO = false;
  195 +
  196 + var Main = (function(){
  197 +
  198 + return {
  199 + loadRandomProposal: function (topic_id, private_token) {
  200 + var $noProposals = $('.no-proposals');
  201 + var $loading = $('.loading');
  202 + var $randomProposal = $('.random-proposal');
  203 + var $body = $(document.body);
  204 + var contextMain = this;
  205 +
  206 + // reset view
  207 + $noProposals.hide();
  208 + $loading.show();
  209 + $randomProposal.html('');
  210 +
  211 + var url = host + '/api/v1/articles/' + topic_id + '/children' + '?private_token=' + private_token + '&limit=1&order=random()&_='+new Date().getTime()+'&fields=id,name,abstract,created_by&content_type=ProposalsDiscussionPlugin::Proposal';
  212 + $.getJSON(url).done(function( data ) {
  213 + $loading.hide();
  214 +
  215 + if(data.articles.length === 0) {
  216 + $noProposals.show();
  217 + return;
  218 + }
  219 +
  220 + var article = data.articles[0];
  221 + $randomProposal.html(supportProposalTemplate(article));
  222 + $body.off('click', '.vote-actions .skip');
  223 + $body.on('click', '.vote-actions .skip', function(e) {
  224 + contextMain.loadRandomProposal(topic_id, private_token);
  225 + e.preventDefault();
  226 + });
  227 + $body.off('click', '.vote-actions .like');
  228 + $body.on('click', '.vote-actions .like', function(e) {
  229 + //Helps to prevent more than one vote per proposal
  230 + if(ProposalApp.hasProposalbeenVoted(article.id)){
  231 + console.log("Proposta " + article.id + " já havia sido votada");
  232 + contextMain.loadRandomProposal(topic_id, private_token);
  233 + e.preventDefault();
  234 + return;
  235 + }
  236 + $.ajax({
  237 + type: 'post',
  238 + url: host + '/api/v1/articles/' + article.id + '/vote',
  239 + data: {
  240 + value: $(this).data('vote-value'),
  241 + private_token: private_token
  242 + }
  243 + }).done(function( /*data*/ ) {
  244 + ProposalApp.addVotedProposal(article.id);
  245 + contextMain.loadRandomProposal(topic_id, private_token);
  246 + });
  247 + e.preventDefault();
  248 + });
  249 +
  250 + $body.off('click', '.vote-result');
  251 + $body.on('click', '.vote-result', function(e) {
  252 +
  253 + var $this = $(this);
  254 + var $proposalDetail = $this.parents('.proposal-detail');
  255 + var $resultsContainer = $proposalDetail.find('.results-container');
  256 +
  257 + // $resultsContainer.toggle();
  258 + // $resultsContainer.toggleClass('hide');
  259 +
  260 + if($resultsContainer.css('display') === 'none') {
  261 +
  262 + $resultsContainer.find('.loading').show();
  263 + $resultsContainer.find('.results-content').hide();
  264 +
  265 + var url = host + '/api/v1/articles/' + topic_id + '/children' + '?private_token=' + private_token + '&limit=10&order=votes_score&fields=id,name,abstract,votes_for,votes_against&content_type=ProposalsDiscussionPlugin::Proposal';
  266 + $.getJSON(url).done(function( data ) {
  267 +
  268 + $resultsContainer.html(resultsTemplate(data));
  269 + $resultsContainer.find('.loading').hide();
  270 + $resultsContainer.find('.results-content').show();
  271 + $resultsContainer.show();
  272 +
  273 + // scroll to the end
  274 + $('html, body').animate({
  275 + scrollTop: $(document).height()
  276 + }, 'fast');
  277 + });
  278 + $('.experience-proposal-container').hide();
  279 + $('.talk-proposal-container').hide();
  280 + } else {
  281 + $('.experience-proposal-container').show();
  282 + $('.talk-proposal-container').show();
  283 + $resultsContainer.hide();
  284 + }
  285 +
  286 + e.preventDefault();
  287 + });
  288 + });
  289 + },
  290 +
  291 + loginCallback: function(loggedIn, token) {
  292 + logged_in = loggedIn;
  293 + $('.login .message').text('');
  294 +
  295 + if(logged_in) {
  296 + if(token){
  297 + private_token = token;
  298 + }
  299 + loginButton.siblings('.save-article-form').show();
  300 + loginButton.siblings('.save-article-form .message').show();
  301 + loginButton.siblings('.login-container').hide();
  302 + $.cookie('_dialoga_session', private_token);
  303 + } else {
  304 + loginButton.siblings('.save-article-form').hide();
  305 + loginButton.siblings('.login-container').show();
  306 + }
  307 + },
  308 + oauthPluginHandleLoginResult: function(loggedIn, token) {
  309 + loginCallback(loggedIn, token);
  310 + },
  311 + guid: function() {
  312 + function s4() {
  313 + return Math.floor((1 + Math.random()) * 0x10000)
  314 + .toString(16)
  315 + .substring(1);
  316 + }
  317 + },
  318 + display_category_tab: function(){
  319 + $('#proposal-group').hide();
  320 + $('#proposal-categories').show();
  321 + $('#nav-proposal-categories a').addClass('active');
  322 + $('#nav-proposal-group a').removeClass('active');
  323 + $('.proposal-category-items').hide();
  324 + $('.proposal-category .arrow-box').hide();
  325 + $('.proposal-detail').hide();
  326 +
  327 + $('#content').show();
  328 + $('nav').show();
  329 + },
  330 + display_proposals_tab: function(){
  331 + $('#proposal-categories').hide();
  332 + $('#proposal-group').show();
  333 + $('#nav-proposal-group a').addClass('active');
  334 + $('#nav-proposal-categories a').removeClass('active');
  335 + $('#content').show();
  336 + $('nav').show();
  337 + },
  338 + display_proposal: function(proposal_id){
  339 + $('#proposal-categories').hide();
  340 + $('#proposal-group').hide();
  341 + $('nav').hide();
  342 + $('#content').hide();
  343 + $('.make-proposal-form').hide();
  344 + $('.login-container').hide();
  345 + $('.proposal-detail').hide();
  346 + $('.proposal-detail-base').hide();
  347 + $('#' + proposal_id).show();
  348 + $('.proposal-header').show();
  349 + $('.make-proposal-container').show();
  350 + $('.support-proposal-container').show();
  351 + $('.results-container').hide();
  352 + $('.results-container .loading').hide();
  353 + $('.results-container .results-content').hide();
  354 + $('.experience-proposal-container').show();
  355 + $('.talk-proposal-container').show();
  356 + var topic_id = proposal_id.split('-').pop();
  357 + this.loadRandomProposal(topic_id, private_token);
  358 + },
  359 + display_proposal_detail: function(proposal_id){
  360 + $('#proposal-categories').hide();
  361 + $('#proposal-group').hide();
  362 + $('nav').hide();
  363 + $('#content').hide();
  364 + $('.make-proposal-form').hide();
  365 + $('.proposal-header').hide();
  366 + $('.make-proposal-container').hide();
  367 + $('.support-proposal-container').hide();
  368 + $('.results-container').hide();
  369 + $('.experience-proposal-container').hide();
  370 + $('.talk-proposal-container').hide();
  371 + $('.body').show();
  372 +
  373 + var url = host + '/api/v1/articles/' + proposal_id + '?private_token=' + private_token + '&fields=id,body&content_type=ProposalsDiscussionPlugin::Topic';
  374 + $.getJSON(url).done(function( data ) {
  375 + $('.body-content').replaceWith(data.article.body);
  376 + })
  377 + .fail(function( jqxhr, textStatus, error ) {
  378 + var err = textStatus + ', ' + error;
  379 + console.log( 'Request Failed: ' + err );
  380 + });
  381 + },
  382 + display_proposal_by_category: function(item){
  383 + var $item = $('#' + item);
  384 +
  385 + if($item.hasClass('proposal-category-items')){
  386 + //Display Topics or Discussion by category
  387 + $('nav').show();
  388 + $('#content').show();
  389 + $('#proposal-categories').show();
  390 + $('#nav-proposal-categories a').addClass('active');
  391 + $('#nav-proposal-group a').removeClass('active');
  392 + $('.proposal-category-items').hide();
  393 + $('.proposal-detail').hide();
  394 + $item.toggle( 'blind', 1000 );
  395 + $('.proposal-category .arrow-box').hide();
  396 + var categorySlug = $item.data('category');
  397 + $('#proposal-category-' + categorySlug).find('.arrow-box').show();
  398 + }
  399 + },
  400 + addBarraDoGoverno: function(){
  401 +
  402 + if( BARRA_ADDED ) { return; }
  403 +
  404 + var HTML_BODY_PREPEND = '' +
  405 + '<div id="barra-brasil" style="background:#7F7F7F; height: 20px; padding:0 0 0 10px;display:block;"> ' +
  406 + '<ul id="menu-barra-temp" style="list-style:none;">' +
  407 + '<li style="display:inline; float:left;padding-right:10px; margin-right:10px; border-right:1px solid #EDEDED"><a href="http://brasil.gov.br" style="font-family:sans,sans-serif; text-decoration:none; color:white;">Portal do Governo Brasileiro</a></li> ' +
  408 + '<li><a style="font-family:sans,sans-serif; text-decoration:none; color:white;" href="http://epwg.governoeletronico.gov.br/barra/atualize.html">Atualize sua Barra de Governo</a></li>' +
  409 + '</ul>' +
  410 + '</div>';
  411 +
  412 + var HTML_BODY_APPEND = ''+
  413 + '<div id="footer-brasil"></div>' +
  414 + '<script defer="defer" src="http://barra.brasil.gov.br/barra.js" type="text/javascript"></script>';
  415 +
  416 + var STYLE_TEMA_AZUL = '' +
  417 + '<style>'+
  418 + '#footer-brasil {'+
  419 + 'background: none repeat scroll 0% 0% #0042b1;'+
  420 + 'padding: 1em 0px;'+
  421 + 'max-width: 100%;'+
  422 + 'margin-top: 40px;'+
  423 + '}'+
  424 + '#barra-brasil ul {'+
  425 + 'width: auto;'+
  426 + '}'+
  427 + '<style>';
  428 +
  429 + var $body = $(document.body);
  430 + $body.prepend(HTML_BODY_PREPEND);
  431 + $body.append(HTML_BODY_APPEND);
  432 + $body.append(STYLE_TEMA_AZUL);
  433 +
  434 + BARRA_ADDED = true;
  435 + },
  436 + updateHash: function(hash){
  437 + var id = hash.replace(/^.*#/, '');
  438 + var elem = document.getElementById(id);
  439 +
  440 + // preserve the query param
  441 + // if (HIDE_BARRA_DO_GOVERNO && (hash.indexOf('?barra=false') === -1)){
  442 + // hash += '?barra=false';
  443 + // }
  444 +
  445 + if ( !elem ) {
  446 + window.location.hash = hash;
  447 + return;
  448 + }
  449 +
  450 + elem.id = id+'-tmp';
  451 + window.location.hash = hash;
  452 + elem.id = id;
  453 + },
  454 + locationHashChanged: function(){
  455 + var hash = window.location.hash;
  456 + this.navigateTo(hash);
  457 + },
  458 + navigateTo: function(hash){
  459 + var regexProposals = /#\/programas/;
  460 + var regexCategory = /#\/temas/;
  461 + var regexHideBarra = /barra=false$/;
  462 +
  463 + if( !(regexHideBarra.exec(hash) !== null) && !HIDE_BARRA_DO_GOVERNO ){
  464 + this.addBarraDoGoverno();
  465 + }else{
  466 + HIDE_BARRA_DO_GOVERNO = true;
  467 + }
  468 +
  469 + // remove query params
  470 + hash = hash.split('?')[0];
  471 +
  472 + var parts = hash.split('/');
  473 +
  474 + var isProposal = regexProposals.exec(hash) !== null;
  475 + var isCategory = regexCategory.exec(hash) !== null;
  476 +
  477 + if( isProposal ){
  478 +
  479 + // go to proposal
  480 + var proposalId = parts[2];
  481 + this.navigateToProposal(proposalId);
  482 + }
  483 +
  484 + if( isCategory ){
  485 +
  486 + // go to category
  487 + var categoryId = parts[3];
  488 + this.navigateToCategory(categoryId);
  489 + }
  490 +
  491 + // default
  492 + if( !isProposal && !isCategory ){
  493 + // show the 'index' -> category tab
  494 + this.display_category_tab();
  495 + }
  496 +
  497 + $('html, body').animate({ scrollTop: 0 }, 'fast');
  498 + },
  499 + navigateToProposal: function(proposalId){
  500 + if(proposalId === undefined){
  501 + this.display_proposals_tab();
  502 + }else{
  503 + this.display_proposal('proposal-item-' + proposalId);
  504 +
  505 + // show sub-page
  506 + var regexSubpages = /sobre-o-programa$/;
  507 + var m;
  508 + if((m = regexSubpages.exec(window.location.hash)) !== null ){
  509 + this.display_proposal_detail(proposalId);
  510 + }
  511 + }
  512 + },
  513 + navigateToCategory: function(categoryId){
  514 + if(categoryId === undefined){
  515 + this.display_category_tab();
  516 + }else{
  517 + this.display_proposal_by_category('proposal-item-' + categoryId);
  518 + }
234 519 }
235   - }).done(function( /*data*/ ) {
236   - addVotedProposal(article.id);
237   - loadRandomProposal(topic_id, private_token);
238   - });
239   - e.preventDefault();
240   - });
241   -
242   - $body.off('click', '.vote-result');
243   - $body.on('click', '.vote-result', function(e) {
244   -
245   - var $this = $(this);
246   - var $proposalDetail = $this.parents('.proposal-detail');
247   - var $resultsContainer = $proposalDetail.find('.results-container');
248   -
249   - // $resultsContainer.toggle();
250   - // $resultsContainer.toggleClass('hide');
251   -
252   - if($resultsContainer.css('display') === 'none') {
253   -
254   - $resultsContainer.find('.loading').show();
255   - $resultsContainer.find('.results-content').hide();
256   -
257   - var url = host + '/api/v1/articles/' + topic_id + '/children' + '?private_token=' + private_token + '&limit=10&order=votes_score&fields=id,name,abstract,votes_for,votes_against&content_type=ProposalsDiscussionPlugin::Proposal';
258   - $.getJSON(url).done(function( data ) {
259   -
260   - $resultsContainer.html(resultsTemplate(data));
261   - $resultsContainer.find('.loading').hide();
262   - $resultsContainer.find('.results-content').show();
263   - $resultsContainer.show();
264   -
265   - // scroll to the end
266   - $('html, body').animate({
267   - scrollTop: $(document).height()
268   - }, 'fast');
269   - });
270   - $('.experience-proposal-container').hide();
271   - $('.talk-proposal-container').hide();
272   - } else {
273   - $('.experience-proposal-container').show();
274   - $('.talk-proposal-container').show();
275   - $resultsContainer.hide();
276   - }
  520 + }
  521 + })();
277 522  
278   - e.preventDefault();
279   - });
280   - });
281   - }
282 523  
283 524 $(document).ready(function($) {
284 525 if($.cookie('_dialoga_session')) {
... ... @@ -310,276 +551,14 @@ define([&#39;handlebars&#39;], function(Handlebars){
310 551 });
311 552 });
312 553  
313   - function loginCallback(loggedIn, token) {
314   - logged_in = loggedIn;
315   - $('.login .message').text('');
316   -
317   - if(logged_in) {
318   - if(token){
319   - private_token = token;
320   - }
321   - loginButton.siblings('.save-article-form').show();
322   - loginButton.siblings('.save-article-form .message').show();
323   - loginButton.siblings('.login-container').hide();
324   - $.cookie('_dialoga_session', private_token);
325   - } else {
326   - loginButton.siblings('.save-article-form').hide();
327   - loginButton.siblings('.login-container').show();
328   - }
329   - }
330   -
331   - window.oauthPluginHandleLoginResult = function(loggedIn, token) {
332   - loginCallback(loggedIn, token);
333   - }
334   -
335   - function guid() {
336   - function s4() {
337   - return Math.floor((1 + Math.random()) * 0x10000)
338   - .toString(16)
339   - .substring(1);
340   - }
341   - return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
342   - }
343   -
344   - function display_category_tab(){
345   - $('#proposal-group').hide();
346   - $('#proposal-categories').show();
347   - $('#nav-proposal-categories a').addClass('active');
348   - $('#nav-proposal-group a').removeClass('active');
349   - $('.proposal-category-items').hide();
350   - $('.proposal-category .arrow-box').hide();
351   - $('.proposal-detail').hide();
352   -
353   - $('#content').show();
354   - $('nav').show();
355   - }
356   -
357   - function display_proposals_tab(){
358   - $('#proposal-categories').hide();
359   - $('#proposal-group').show();
360   - $('#nav-proposal-group a').addClass('active');
361   - $('#nav-proposal-categories a').removeClass('active');
362   - $('#content').show();
363   - $('nav').show();
364   - }
365   -
366   - function display_proposal(proposal_id){
367   - $('#proposal-categories').hide();
368   - $('#proposal-group').hide();
369   - $('nav').hide();
370   - $('#content').hide();
371   - $('.make-proposal-form').hide();
372   - $('.login-container').hide();
373   - $('.proposal-detail').hide();
374   -
375   - $('.proposal-detail-base').hide();
376   - $('#' + proposal_id).show();
377   - $('.proposal-header').show();
378   - $('.make-proposal-container').show();
379   - $('.support-proposal-container').show();
380   - $('.results-container').hide();
381   - $('.results-container .loading').hide();
382   - $('.results-container .results-content').hide();
383   - $('.experience-proposal-container').show();
384   - $('.talk-proposal-container').show();
385   -
386   - var topic_id = proposal_id.split('-').pop();
387   - loadRandomProposal(topic_id, private_token);
388   - }
389   -
390   - function display_proposal_detail(proposal_id){
391   - $('#proposal-categories').hide();
392   - $('#proposal-group').hide();
393   - $('nav').hide();
394   - $('#content').hide();
395   - $('.make-proposal-form').hide();
396   - $('.proposal-header').hide();
397   - $('.make-proposal-container').hide();
398   - $('.support-proposal-container').hide();
399   - $('.results-container').hide();
400   - $('.experience-proposal-container').hide();
401   - $('.talk-proposal-container').hide();
402   - $('#proposal-item-' + proposal_id + '.proposal-detail').show();
403   - $('#proposal-item-' + proposal_id +' .body').show();
404   -
405   - var url = host + '/api/v1/articles/' + proposal_id + '?private_token=' + private_token + '&fields=id,body&content_type=ProposalsDiscussionPlugin::Topic';
406   - $.getJSON(url).done(function( data ) {
407   - $('#proposal-item-' + proposal_id +' .body-content').html(data.article.body);
408   - })
409   - .fail(function( jqxhr, textStatus, error ) {
410   - var err = textStatus + ', ' + error;
411   - console.log( 'Request Failed: ' + err );
412   - });
413   - }
414   - function display_proposal_by_category(item){
415   - var $item = $('#' + item);
416   -
417   - if($item.hasClass('proposal-category-items')){
418   - //Display Topics or Discussion by category
419   - $('nav').show();
420   - $('#content').show();
421   - $('#proposal-categories').show();
422   - $('#nav-proposal-categories a').addClass('active');
423   - $('#nav-proposal-group a').removeClass('active');
424   - $('.proposal-category-items').hide();
425   - $('.proposal-detail').hide();
426   - $item.toggle( 'blind', 1000 );
427   - $('.proposal-category .arrow-box').hide();
428   - var categorySlug = $item.data('category');
429   - $('#proposal-category-' + categorySlug).find('.arrow-box').show();
430   - }
431   - }
432   -
433   - var BARRA_ADDED = false;
434   - function addBarraDoGoverno(){
435   -
436   - if( BARRA_ADDED ) { return; }
437   -
438   - var HTML_BODY_PREPEND = '' +
439   - '<div id="barra-brasil" style="background:#7F7F7F; height: 20px; padding:0 0 0 10px;display:block;"> ' +
440   - '<ul id="menu-barra-temp" style="list-style:none;">' +
441   - '<li style="display:inline; float:left;padding-right:10px; margin-right:10px; border-right:1px solid #EDEDED"><a href="http://brasil.gov.br" style="font-family:sans,sans-serif; text-decoration:none; color:white;">Portal do Governo Brasileiro</a></li> ' +
442   - '<li><a style="font-family:sans,sans-serif; text-decoration:none; color:white;" href="http://epwg.governoeletronico.gov.br/barra/atualize.html">Atualize sua Barra de Governo</a></li>' +
443   - '</ul>' +
444   - '</div>';
445   -
446   - var HTML_BODY_APPEND = ''+
447   - '<div id="footer-brasil"></div>' +
448   - '<script defer="defer" src="http://barra.brasil.gov.br/barra.js" type="text/javascript"></script>';
449   -
450   - var STYLE_TEMA_AZUL = '' +
451   - '<style>'+
452   - '#footer-brasil {'+
453   - 'background: none repeat scroll 0% 0% #0042b1;'+
454   - 'padding: 1em 0px;'+
455   - 'max-width: 100%;'+
456   - 'margin-top: 40px;'+
457   - '}'+
458   - '#barra-brasil ul {'+
459   - 'width: auto;'+
460   - '}'+
461   - '<style>';
462   -
463   - var $body = $(document.body);
464   - $body.prepend(HTML_BODY_PREPEND);
465   - $body.append(HTML_BODY_APPEND);
466   - $body.append(STYLE_TEMA_AZUL);
467   -
468   - BARRA_ADDED = true;
469   - }
470   -
471   - function updateHash(hash){
472   - var id = hash.replace(/^.*#/, '');
473   - var elem = document.getElementById(id);
474   -
475   - // preserve the query param
476   - // if (HIDE_BARRA_DO_GOVERNO && (hash.indexOf('?barra=false') === -1)){
477   - // hash += '?barra=false';
478   - // }
479   -
480   - if ( !elem ) {
481   - window.location.hash = hash;
482   - return;
483   - }
484   -
485   - elem.id = id+'-tmp';
486   - window.location.hash = hash;
487   - elem.id = id;
488   - }
489   -
490   - function locationHashChanged(){
491   - var hash = window.location.hash;
492   - navigateTo(hash);
493   - }
494   -
495   - var HIDE_BARRA_DO_GOVERNO = false;
496   - function navigateTo(hash){
497   - var regexProposals = /#\/programas/;
498   - var regexCategory = /#\/temas/;
499   - var regexHideBarra = /barra=false$/;
500   -
501   - if( !(regexHideBarra.exec(hash) !== null) && !HIDE_BARRA_DO_GOVERNO ){
502   - addBarraDoGoverno();
503   - }else{
504   - HIDE_BARRA_DO_GOVERNO = true;
505   - }
506   -
507   - // remove query params
508   - hash = hash.split('?')[0];
509   -
510   - var parts = hash.split('/');
511   -
512   - var isProposal = regexProposals.exec(hash) !== null;
513   - var isCategory = regexCategory.exec(hash) !== null;
514   -
515   - if( isProposal ){
516   -
517   - // go to proposal
518   - var proposalId = parts[2];
519   - navigateToProposal(proposalId);
520   - }
521   -
522   - if( isCategory ){
523   -
524   - // go to category
525   - var categoryId = parts[3];
526   - navigateToCategory(categoryId);
527   - }
528   -
529   - // default
530   - if( !isProposal && !isCategory ){
531   - // show the 'index' -> category tab
532   - display_category_tab();
533   - }
534   -
535   - $('html, body').animate({ scrollTop: 0 }, 'fast');
536   - }
537   -
538   - function navigateToProposal(proposalId){
539   - var regexSubpages = /sobre-o-programa$/;
540   - if(proposalId === undefined){
541   - display_proposals_tab();
542   - }else if(regexSubpages.exec(window.location.hash) == null){
543   - display_proposal('proposal-item-' + proposalId);
544   - }else{
545   - display_proposal_detail(proposalId);
546   - }
547   - }
548   -
549   - function navigateToCategory(categoryId){
550   - if(categoryId === undefined){
551   - display_category_tab();
552   - }else{
553   - display_proposal_by_category('proposal-item-' + categoryId);
554   - }
555   - }
556   -
557   - function addVotedProposal(id) {
558   - var votedProposals;
559   - if (typeof($.cookie("votedProposals")) == "undefined"){
560   - votedProposals = [];
561   - }
562   - else{
563   - votedProposals = JSON.parse($.cookie("votedProposals"));
564   - }
565   - if (votedProposals.indexOf(id)==-1){
566   - votedProposals.push(id);
567   - }
568   - $.cookie("votedProposals", JSON.stringify(votedProposals), {expires : 999 }) ;
569   - }
  554 + if('onhashchange' in window){
570 555  
571   - function hasProposalbeenVoted(id) {
572   - if (typeof($.cookie("votedProposals")) == "undefined") {
573   - return false;
  556 + window.onhashchange = function(){
  557 + Main.locationHashChanged.apply(Main);
574 558 }
575   - votedProposals = JSON.parse($.cookie("votedProposals"));
576   - return votedProposals.indexOf(id)!=-1;
577   - }
578   -
579   - if('onhashchange' in window){
580   - window.onhashchange = locationHashChanged;
581 559 }else{
582 560 console.log('The browser not supports the hashchange event!');
583 561 }
584 562  
  563 + return Main;
585 564 });
... ...
js/proposal-app.js 0 → 100644
... ... @@ -0,0 +1,34 @@
  1 +define(['jquery','jquery_cookie'],function($){
  2 +
  3 +
  4 + ProposalApp = (function (){
  5 +
  6 + return {
  7 + addVotedProposal: function(id) {
  8 + var votedProposals;
  9 + if (typeof($.cookie("votedProposals")) == "undefined"){
  10 + votedProposals = [];
  11 + }
  12 + else{
  13 + votedProposals = JSON.parse($.cookie("votedProposals"));
  14 + }
  15 + if (votedProposals.indexOf(id)==-1){
  16 + votedProposals.push(id);
  17 + }
  18 + $.cookie("votedProposals", JSON.stringify(votedProposals), {expires : 999 }) ;
  19 + return votedProposals;
  20 + },
  21 +
  22 + hasProposalbeenVoted: function(id) {
  23 + if (typeof($.cookie("votedProposals")) == "undefined") {
  24 + return false;
  25 + }
  26 + votedProposals = JSON.parse($.cookie("votedProposals"));
  27 + return votedProposals.indexOf(id)!=-1;
  28 + }
  29 + };
  30 +
  31 + })();
  32 +
  33 + return ProposalApp;
  34 +});
... ...
js/requirejs-config.js
1   -var base = window.location.href;
2   -var host = window.location.host;
3   -var regex = new RegExp(".*" + host + '/', "g");
4   -
5   -if(host){
6   - base = base.replace(regex,'');
  1 +if(window.Url){
  2 + var base = Url.initBase();
  3 + base = Url.removeUrlParameters(base) + '/js/';
7 4 }else{
8   - base = base.replace('index.html','');
  5 + base = '../js'
9 6 }
10   -base = removeUrlParameters(base);
11 7  
12 8 requirejs.config({
13   - baseUrl: ( base + '/js/'),
  9 + baseUrl: base,
14 10 waitSeconds: 0,
15 11 paths: {
16 12 jquery: 'jquery-2.1.3.min',
17   - jquery_ui: 'jquery-ui-1.11.4.custom/jquery-ui.min',
18   - jquery_cookie: 'jquery.cookie',
  13 + jquery_ui: 'jquery-ui-1.11.4.custom/jquery-ui.min',
  14 + jquery_cookie: 'jquery.cookie',
19 15 handlebars: 'handlebars-v3.0.1',
20 16 handlebars_helpers: 'handlebars-helpers',
21 17 jquery_maxlength: 'jquery.maxlength.min',
22 18 layout: 'layout',
23   - main: 'main'
  19 + main: 'main',
  20 + proposal_app: 'proposal-app'
24 21 },
25 22 shim: {
26 23 'handlebars':{
... ... @@ -38,9 +35,13 @@ requirejs.config({
38 35 },
39 36 'handlebars_helpers':{
40 37 deps: ['handlebars']
  38 + },
  39 + 'proposal_app' :{
  40 + deps: ['jquery'],
  41 + exports: 'ProposalApp'
41 42 }
42 43 }
43 44 });
44 45  
45   -requirejs(['jquery', 'jquery_ui', 'jquery_cookie', 'handlebars', 'handlebars_helpers']);
46   -requirejs(['jquery_maxlength', 'layout', 'main']);
  46 +requirejs(['jquery', 'proposal_app', 'jquery_ui','handlebars_helpers']);
  47 +requirejs(['jquery_maxlength', 'layout','main']);
... ...
test/SpecRunner.html 0 → 100644
... ... @@ -0,0 +1,105 @@
  1 +<!DOCTYPE html>
  2 +<html>
  3 +<head>
  4 + <meta charset="utf-8">
  5 + <title>Jasmine Spec Runner v2.2.0</title>
  6 +
  7 + <link rel="shortcut icon" type="image/png" href="lib/jasmine-2.2.0/jasmine_favicon.png">
  8 + <link rel="stylesheet" href="lib/jasmine-2.2.0/jasmine.css">
  9 +
  10 + <!-- // <script src="lib/jasmine-2.2.0/jasmine.js"></script>
  11 + // <script src="lib/jasmine-2.2.0/jasmine-html.js"></script> -->
  12 + <!-- // <script src="lib/jasmine-2.2.0/boot.js"></script> -->
  13 +
  14 + <script type="text/javascript">
  15 +
  16 + var require = {
  17 + paths: {
  18 + "domReady": "lib/requirejs-plugins/domReady",
  19 + "model": "../js",
  20 + "handlebars": '../js/handlebars-v3.0.1',
  21 + "handlebars_helpers": '../js/handlebars-helpers',
  22 + "jquery":'../js/jquery-2.1.3.min',
  23 + "jquery_cookie":'../js/jquery.cookie',
  24 + "jasmine":'lib/jasmine-2.2.0/jasmine',
  25 + "jasmine-html":'lib/jasmine-2.2.0/jasmine-html',
  26 + "jasmine-jquery":'lib/jasmine-plugins/jasmine-jquery',
  27 + "jasmine-boot":'lib/jasmine-2.2.0/boot'
  28 + },
  29 + shim:{
  30 + jasmine:{
  31 + exports: 'jasmine'
  32 + },
  33 + 'jasmine-html':{
  34 + deps:['jasmine'],
  35 + exports: 'jasmine'
  36 + },
  37 + 'jasmine-boot':{
  38 + deps:['jasmine-html','jasmine'],
  39 + exports:'jasmine'
  40 + }
  41 + }
  42 + };
  43 +
  44 + </script>
  45 +
  46 + <script src="../js/require.js"></script>
  47 +
  48 + <!-- // <script src="../js/handlebars-v3.0.1.js"></script> -->
  49 + <!-- // <script src="../js/handlebars-helpers.js"></script> -->
  50 +
  51 + <!-- include source files here... -->
  52 + <!-- <script src="../js/main.js"></script> -->
  53 +
  54 + <!-- include spec files here... -->
  55 +
  56 + <script type="text/javascript">
  57 +
  58 + require(
  59 + [
  60 + "domReady!",
  61 + "spec/MainSpec"
  62 + ],
  63 + function( jasmine ){
  64 +
  65 + // window.jasmine = jasmineRequire.core(jasmineRequire);
  66 + //
  67 + // jasmineRequire.html(jasmine);
  68 + //
  69 + // var jasmineEnv = jasmine.getEnv();
  70 + // jasmineEnv.updateInterval = 1000;
  71 + //
  72 + // var htmlReporter = new jasmine.HtmlReporter();
  73 + //
  74 + // jasmineEnv.addReporter(htmlReporter);
  75 + //
  76 + // jasmineEnv.specFilter = function (spec) {
  77 + // return htmlReporter.specFilter(spec);
  78 + // };
  79 +
  80 +
  81 + // Set up the HTML reporter - this is reponsible for
  82 + // aggregating the results reported by Jasmine as the
  83 + // tests and suites are executed.
  84 + // jasmine.getEnv().addReporter(
  85 + // new jasmine.HtmlReporter()
  86 + // );
  87 + //
  88 + // // Run all the loaded test specs.
  89 + // jasmine.getEnv().execute();
  90 + if(typeof window.onload == 'function'){
  91 + window.onload();
  92 + }
  93 + }
  94 + );
  95 +
  96 + </script>
  97 +
  98 +
  99 + <!-- // <script src="spec/MainSpec.js"></script> -->
  100 +
  101 +</head>
  102 +
  103 +<body>
  104 +</body>
  105 +</html>
... ...
test/lib/jasmine-2.2.0/boot.js 0 → 100644
... ... @@ -0,0 +1,121 @@
  1 +/**
  2 + Starting with version 2.0, this file "boots" Jasmine, performing all of the necessary initialization before executing the loaded environment and all of a project's specs. This file should be loaded after `jasmine.js` and `jasmine_html.js`, but before any project source files or spec files are loaded. Thus this file can also be used to customize Jasmine for a project.
  3 +
  4 + If a project is using Jasmine via the standalone distribution, this file can be customized directly. If a project is using Jasmine via the [Ruby gem][jasmine-gem], this file can be copied into the support directory via `jasmine copy_boot_js`. Other environments (e.g., Python) will have different mechanisms.
  5 +
  6 + The location of `boot.js` can be specified and/or overridden in `jasmine.yml`.
  7 +
  8 + [jasmine-gem]: http://github.com/pivotal/jasmine-gem
  9 + */
  10 +
  11 +(function() {
  12 +
  13 + /**
  14 + * ## Require &amp; Instantiate
  15 + *
  16 + * Require Jasmine's core files. Specifically, this requires and attaches all of Jasmine's code to the `jasmine` reference.
  17 + */
  18 + window.jasmine = jasmineRequire.core(jasmineRequire);
  19 +
  20 + /**
  21 + * Since this is being run in a browser and the results should populate to an HTML page, require the HTML-specific Jasmine code, injecting the same reference.
  22 + */
  23 + jasmineRequire.html(jasmine);
  24 +
  25 + /**
  26 + * Create the Jasmine environment. This is used to run all specs in a project.
  27 + */
  28 + var env = jasmine.getEnv();
  29 +
  30 + /**
  31 + * ## The Global Interface
  32 + *
  33 + * Build up the functions that will be exposed as the Jasmine public interface. A project can customize, rename or alias any of these functions as desired, provided the implementation remains unchanged.
  34 + */
  35 + var jasmineInterface = jasmineRequire.interface(jasmine, env);
  36 +
  37 + /**
  38 + * Add all of the Jasmine global/public interface to the proper global, so a project can use the public interface directly. For example, calling `describe` in specs instead of `jasmine.getEnv().describe`.
  39 + */
  40 + if (typeof window == "undefined" && typeof exports == "object") {
  41 + extend(exports, jasmineInterface);
  42 + } else {
  43 + extend(window, jasmineInterface);
  44 + }
  45 +
  46 + /**
  47 + * ## Runner Parameters
  48 + *
  49 + * More browser specific code - wrap the query string in an object and to allow for getting/setting parameters from the runner user interface.
  50 + */
  51 +
  52 + var queryString = new jasmine.QueryString({
  53 + getWindowLocation: function() { return window.location; }
  54 + });
  55 +
  56 + var catchingExceptions = queryString.getParam("catch");
  57 + env.catchExceptions(typeof catchingExceptions === "undefined" ? true : catchingExceptions);
  58 +
  59 + /**
  60 + * ## Reporters
  61 + * The `HtmlReporter` builds all of the HTML UI for the runner page. This reporter paints the dots, stars, and x's for specs, as well as all spec names and all failures (if any).
  62 + */
  63 + var htmlReporter = new jasmine.HtmlReporter({
  64 + env: env,
  65 + onRaiseExceptionsClick: function() { queryString.navigateWithNewParam("catch", !env.catchingExceptions()); },
  66 + addToExistingQueryString: function(key, value) { return queryString.fullStringWithNewParam(key, value); },
  67 + getContainer: function() { return document.body; },
  68 + createElement: function() { return document.createElement.apply(document, arguments); },
  69 + createTextNode: function() { return document.createTextNode.apply(document, arguments); },
  70 + timer: new jasmine.Timer()
  71 + });
  72 +
  73 + /**
  74 + * The `jsApiReporter` also receives spec results, and is used by any environment that needs to extract the results from JavaScript.
  75 + */
  76 + env.addReporter(jasmineInterface.jsApiReporter);
  77 + env.addReporter(htmlReporter);
  78 +
  79 + /**
  80 + * Filter which specs will be run by matching the start of the full name against the `spec` query param.
  81 + */
  82 + var specFilter = new jasmine.HtmlSpecFilter({
  83 + filterString: function() { return queryString.getParam("spec"); }
  84 + });
  85 +
  86 + env.specFilter = function(spec) {
  87 + return specFilter.matches(spec.getFullName());
  88 + };
  89 +
  90 + /**
  91 + * Setting up timing functions to be able to be overridden. Certain browsers (Safari, IE 8, phantomjs) require this hack.
  92 + */
  93 + window.setTimeout = window.setTimeout;
  94 + window.setInterval = window.setInterval;
  95 + window.clearTimeout = window.clearTimeout;
  96 + window.clearInterval = window.clearInterval;
  97 +
  98 + /**
  99 + * ## Execution
  100 + *
  101 + * Replace the browser window's `onload`, ensure it's called, and then run all of the loaded specs. This includes initializing the `HtmlReporter` instance and then executing the loaded Jasmine environment. All of this will happen after all of the specs are loaded.
  102 + */
  103 + var currentWindowOnload = window.onload;
  104 +
  105 + window.onload = function() {
  106 + if (currentWindowOnload) {
  107 + currentWindowOnload();
  108 + }
  109 + htmlReporter.initialize();
  110 + env.execute();
  111 + };
  112 +
  113 + /**
  114 + * Helper function for readability above.
  115 + */
  116 + function extend(destination, source) {
  117 + for (var property in source) destination[property] = source[property];
  118 + return destination;
  119 + }
  120 +
  121 +}());
... ...
test/lib/jasmine-2.2.0/console.js 0 → 100644
... ... @@ -0,0 +1,190 @@
  1 +/*
  2 +Copyright (c) 2008-2015 Pivotal Labs
  3 +
  4 +Permission is hereby granted, free of charge, to any person obtaining
  5 +a copy of this software and associated documentation files (the
  6 +"Software"), to deal in the Software without restriction, including
  7 +without limitation the rights to use, copy, modify, merge, publish,
  8 +distribute, sublicense, and/or sell copies of the Software, and to
  9 +permit persons to whom the Software is furnished to do so, subject to
  10 +the following conditions:
  11 +
  12 +The above copyright notice and this permission notice shall be
  13 +included in all copies or substantial portions of the Software.
  14 +
  15 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  16 +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  17 +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  18 +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  19 +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  20 +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  21 +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  22 +*/
  23 +function getJasmineRequireObj() {
  24 + if (typeof module !== 'undefined' && module.exports) {
  25 + return exports;
  26 + } else {
  27 + window.jasmineRequire = window.jasmineRequire || {};
  28 + return window.jasmineRequire;
  29 + }
  30 +}
  31 +
  32 +getJasmineRequireObj().console = function(jRequire, j$) {
  33 + j$.ConsoleReporter = jRequire.ConsoleReporter();
  34 +};
  35 +
  36 +getJasmineRequireObj().ConsoleReporter = function() {
  37 +
  38 + var noopTimer = {
  39 + start: function(){},
  40 + elapsed: function(){ return 0; }
  41 + };
  42 +
  43 + function ConsoleReporter(options) {
  44 + var print = options.print,
  45 + showColors = options.showColors || false,
  46 + onComplete = options.onComplete || function() {},
  47 + timer = options.timer || noopTimer,
  48 + specCount,
  49 + failureCount,
  50 + failedSpecs = [],
  51 + pendingCount,
  52 + ansi = {
  53 + green: '\x1B[32m',
  54 + red: '\x1B[31m',
  55 + yellow: '\x1B[33m',
  56 + none: '\x1B[0m'
  57 + },
  58 + failedSuites = [];
  59 +
  60 + print('ConsoleReporter is deprecated and will be removed in a future version.');
  61 +
  62 + this.jasmineStarted = function() {
  63 + specCount = 0;
  64 + failureCount = 0;
  65 + pendingCount = 0;
  66 + print('Started');
  67 + printNewline();
  68 + timer.start();
  69 + };
  70 +
  71 + this.jasmineDone = function() {
  72 + printNewline();
  73 + for (var i = 0; i < failedSpecs.length; i++) {
  74 + specFailureDetails(failedSpecs[i]);
  75 + }
  76 +
  77 + if(specCount > 0) {
  78 + printNewline();
  79 +
  80 + var specCounts = specCount + ' ' + plural('spec', specCount) + ', ' +
  81 + failureCount + ' ' + plural('failure', failureCount);
  82 +
  83 + if (pendingCount) {
  84 + specCounts += ', ' + pendingCount + ' pending ' + plural('spec', pendingCount);
  85 + }
  86 +
  87 + print(specCounts);
  88 + } else {
  89 + print('No specs found');
  90 + }
  91 +
  92 + printNewline();
  93 + var seconds = timer.elapsed() / 1000;
  94 + print('Finished in ' + seconds + ' ' + plural('second', seconds));
  95 + printNewline();
  96 +
  97 + for(i = 0; i < failedSuites.length; i++) {
  98 + suiteFailureDetails(failedSuites[i]);
  99 + }
  100 +
  101 + onComplete(failureCount === 0);
  102 + };
  103 +
  104 + this.specDone = function(result) {
  105 + specCount++;
  106 +
  107 + if (result.status == 'pending') {
  108 + pendingCount++;
  109 + print(colored('yellow', '*'));
  110 + return;
  111 + }
  112 +
  113 + if (result.status == 'passed') {
  114 + print(colored('green', '.'));
  115 + return;
  116 + }
  117 +
  118 + if (result.status == 'failed') {
  119 + failureCount++;
  120 + failedSpecs.push(result);
  121 + print(colored('red', 'F'));
  122 + }
  123 + };
  124 +
  125 + this.suiteDone = function(result) {
  126 + if (result.failedExpectations && result.failedExpectations.length > 0) {
  127 + failureCount++;
  128 + failedSuites.push(result);
  129 + }
  130 + };
  131 +
  132 + return this;
  133 +
  134 + function printNewline() {
  135 + print('\n');
  136 + }
  137 +
  138 + function colored(color, str) {
  139 + return showColors ? (ansi[color] + str + ansi.none) : str;
  140 + }
  141 +
  142 + function plural(str, count) {
  143 + return count == 1 ? str : str + 's';
  144 + }
  145 +
  146 + function repeat(thing, times) {
  147 + var arr = [];
  148 + for (var i = 0; i < times; i++) {
  149 + arr.push(thing);
  150 + }
  151 + return arr;
  152 + }
  153 +
  154 + function indent(str, spaces) {
  155 + var lines = (str || '').split('\n');
  156 + var newArr = [];
  157 + for (var i = 0; i < lines.length; i++) {
  158 + newArr.push(repeat(' ', spaces).join('') + lines[i]);
  159 + }
  160 + return newArr.join('\n');
  161 + }
  162 +
  163 + function specFailureDetails(result) {
  164 + printNewline();
  165 + print(result.fullName);
  166 +
  167 + for (var i = 0; i < result.failedExpectations.length; i++) {
  168 + var failedExpectation = result.failedExpectations[i];
  169 + printNewline();
  170 + print(indent(failedExpectation.message, 2));
  171 + print(indent(failedExpectation.stack, 2));
  172 + }
  173 +
  174 + printNewline();
  175 + }
  176 +
  177 + function suiteFailureDetails(result) {
  178 + for (var i = 0; i < result.failedExpectations.length; i++) {
  179 + printNewline();
  180 + print(colored('red', 'An error was thrown in an afterAll'));
  181 + printNewline();
  182 + print(colored('red', 'AfterAll ' + result.failedExpectations[i].message));
  183 +
  184 + }
  185 + printNewline();
  186 + }
  187 + }
  188 +
  189 + return ConsoleReporter;
  190 +};
... ...
test/lib/jasmine-2.2.0/jasmine-html.js 0 → 100644
... ... @@ -0,0 +1,416 @@
  1 +/*
  2 +Copyright (c) 2008-2015 Pivotal Labs
  3 +
  4 +Permission is hereby granted, free of charge, to any person obtaining
  5 +a copy of this software and associated documentation files (the
  6 +"Software"), to deal in the Software without restriction, including
  7 +without limitation the rights to use, copy, modify, merge, publish,
  8 +distribute, sublicense, and/or sell copies of the Software, and to
  9 +permit persons to whom the Software is furnished to do so, subject to
  10 +the following conditions:
  11 +
  12 +The above copyright notice and this permission notice shall be
  13 +included in all copies or substantial portions of the Software.
  14 +
  15 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  16 +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  17 +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  18 +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  19 +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  20 +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  21 +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  22 +*/
  23 +jasmineRequire.html = function(j$) {
  24 + j$.ResultsNode = jasmineRequire.ResultsNode();
  25 + j$.HtmlReporter = jasmineRequire.HtmlReporter(j$);
  26 + j$.QueryString = jasmineRequire.QueryString();
  27 + j$.HtmlSpecFilter = jasmineRequire.HtmlSpecFilter();
  28 +};
  29 +
  30 +jasmineRequire.HtmlReporter = function(j$) {
  31 +
  32 + var noopTimer = {
  33 + start: function() {},
  34 + elapsed: function() { return 0; }
  35 + };
  36 +
  37 + function HtmlReporter(options) {
  38 + var env = options.env || {},
  39 + getContainer = options.getContainer,
  40 + createElement = options.createElement,
  41 + createTextNode = options.createTextNode,
  42 + onRaiseExceptionsClick = options.onRaiseExceptionsClick || function() {},
  43 + addToExistingQueryString = options.addToExistingQueryString || defaultQueryString,
  44 + timer = options.timer || noopTimer,
  45 + results = [],
  46 + specsExecuted = 0,
  47 + failureCount = 0,
  48 + pendingSpecCount = 0,
  49 + htmlReporterMain,
  50 + symbols,
  51 + failedSuites = [];
  52 +
  53 + this.initialize = function() {
  54 + clearPrior();
  55 + htmlReporterMain = createDom('div', {className: 'jasmine_html-reporter'},
  56 + createDom('div', {className: 'banner'},
  57 + createDom('a', {className: 'title', href: 'http://jasmine.github.io/', target: '_blank'}),
  58 + createDom('span', {className: 'version'}, j$.version)
  59 + ),
  60 + createDom('ul', {className: 'symbol-summary'}),
  61 + createDom('div', {className: 'alert'}),
  62 + createDom('div', {className: 'results'},
  63 + createDom('div', {className: 'failures'})
  64 + )
  65 + );
  66 + getContainer().appendChild(htmlReporterMain);
  67 +
  68 + symbols = find('.symbol-summary');
  69 + };
  70 +
  71 + var totalSpecsDefined;
  72 + this.jasmineStarted = function(options) {
  73 + totalSpecsDefined = options.totalSpecsDefined || 0;
  74 + timer.start();
  75 + };
  76 +
  77 + var summary = createDom('div', {className: 'summary'});
  78 +
  79 + var topResults = new j$.ResultsNode({}, '', null),
  80 + currentParent = topResults;
  81 +
  82 + this.suiteStarted = function(result) {
  83 + currentParent.addChild(result, 'suite');
  84 + currentParent = currentParent.last();
  85 + };
  86 +
  87 + this.suiteDone = function(result) {
  88 + if (result.status == 'failed') {
  89 + failedSuites.push(result);
  90 + }
  91 +
  92 + if (currentParent == topResults) {
  93 + return;
  94 + }
  95 +
  96 + currentParent = currentParent.parent;
  97 + };
  98 +
  99 + this.specStarted = function(result) {
  100 + currentParent.addChild(result, 'spec');
  101 + };
  102 +
  103 + var failures = [];
  104 + this.specDone = function(result) {
  105 + if(noExpectations(result) && typeof console !== 'undefined' && typeof console.error !== 'undefined') {
  106 + console.error('Spec \'' + result.fullName + '\' has no expectations.');
  107 + }
  108 +
  109 + if (result.status != 'disabled') {
  110 + specsExecuted++;
  111 + }
  112 +
  113 + symbols.appendChild(createDom('li', {
  114 + className: noExpectations(result) ? 'empty' : result.status,
  115 + id: 'spec_' + result.id,
  116 + title: result.fullName
  117 + }
  118 + ));
  119 +
  120 + if (result.status == 'failed') {
  121 + failureCount++;
  122 +
  123 + var failure =
  124 + createDom('div', {className: 'spec-detail failed'},
  125 + createDom('div', {className: 'description'},
  126 + createDom('a', {title: result.fullName, href: specHref(result)}, result.fullName)
  127 + ),
  128 + createDom('div', {className: 'messages'})
  129 + );
  130 + var messages = failure.childNodes[1];
  131 +
  132 + for (var i = 0; i < result.failedExpectations.length; i++) {
  133 + var expectation = result.failedExpectations[i];
  134 + messages.appendChild(createDom('div', {className: 'result-message'}, expectation.message));
  135 + messages.appendChild(createDom('div', {className: 'stack-trace'}, expectation.stack));
  136 + }
  137 +
  138 + failures.push(failure);
  139 + }
  140 +
  141 + if (result.status == 'pending') {
  142 + pendingSpecCount++;
  143 + }
  144 + };
  145 +
  146 + this.jasmineDone = function() {
  147 + var banner = find('.banner');
  148 + banner.appendChild(createDom('span', {className: 'duration'}, 'finished in ' + timer.elapsed() / 1000 + 's'));
  149 +
  150 + var alert = find('.alert');
  151 +
  152 + alert.appendChild(createDom('span', { className: 'exceptions' },
  153 + createDom('label', { className: 'label', 'for': 'raise-exceptions' }, 'raise exceptions'),
  154 + createDom('input', {
  155 + className: 'raise',
  156 + id: 'raise-exceptions',
  157 + type: 'checkbox'
  158 + })
  159 + ));
  160 + var checkbox = find('#raise-exceptions');
  161 +
  162 + checkbox.checked = !env.catchingExceptions();
  163 + checkbox.onclick = onRaiseExceptionsClick;
  164 +
  165 + if (specsExecuted < totalSpecsDefined) {
  166 + var skippedMessage = 'Ran ' + specsExecuted + ' of ' + totalSpecsDefined + ' specs - run all';
  167 + alert.appendChild(
  168 + createDom('span', {className: 'bar skipped'},
  169 + createDom('a', {href: '?', title: 'Run all specs'}, skippedMessage)
  170 + )
  171 + );
  172 + }
  173 + var statusBarMessage = '';
  174 + var statusBarClassName = 'bar ';
  175 +
  176 + if (totalSpecsDefined > 0) {
  177 + statusBarMessage += pluralize('spec', specsExecuted) + ', ' + pluralize('failure', failureCount);
  178 + if (pendingSpecCount) { statusBarMessage += ', ' + pluralize('pending spec', pendingSpecCount); }
  179 + statusBarClassName += (failureCount > 0) ? 'failed' : 'passed';
  180 + } else {
  181 + statusBarClassName += 'skipped';
  182 + statusBarMessage += 'No specs found';
  183 + }
  184 +
  185 + alert.appendChild(createDom('span', {className: statusBarClassName}, statusBarMessage));
  186 +
  187 + for(i = 0; i < failedSuites.length; i++) {
  188 + var failedSuite = failedSuites[i];
  189 + for(var j = 0; j < failedSuite.failedExpectations.length; j++) {
  190 + var errorBarMessage = 'AfterAll ' + failedSuite.failedExpectations[j].message;
  191 + var errorBarClassName = 'bar errored';
  192 + alert.appendChild(createDom('span', {className: errorBarClassName}, errorBarMessage));
  193 + }
  194 + }
  195 +
  196 + var results = find('.results');
  197 + results.appendChild(summary);
  198 +
  199 + summaryList(topResults, summary);
  200 +
  201 + function summaryList(resultsTree, domParent) {
  202 + var specListNode;
  203 + for (var i = 0; i < resultsTree.children.length; i++) {
  204 + var resultNode = resultsTree.children[i];
  205 + if (resultNode.type == 'suite') {
  206 + var suiteListNode = createDom('ul', {className: 'suite', id: 'suite-' + resultNode.result.id},
  207 + createDom('li', {className: 'suite-detail'},
  208 + createDom('a', {href: specHref(resultNode.result)}, resultNode.result.description)
  209 + )
  210 + );
  211 +
  212 + summaryList(resultNode, suiteListNode);
  213 + domParent.appendChild(suiteListNode);
  214 + }
  215 + if (resultNode.type == 'spec') {
  216 + if (domParent.getAttribute('class') != 'specs') {
  217 + specListNode = createDom('ul', {className: 'specs'});
  218 + domParent.appendChild(specListNode);
  219 + }
  220 + var specDescription = resultNode.result.description;
  221 + if(noExpectations(resultNode.result)) {
  222 + specDescription = 'SPEC HAS NO EXPECTATIONS ' + specDescription;
  223 + }
  224 + if(resultNode.result.status === 'pending' && resultNode.result.pendingReason !== '') {
  225 + specDescription = specDescription + ' PENDING WITH MESSAGE: ' + resultNode.result.pendingReason;
  226 + }
  227 + specListNode.appendChild(
  228 + createDom('li', {
  229 + className: resultNode.result.status,
  230 + id: 'spec-' + resultNode.result.id
  231 + },
  232 + createDom('a', {href: specHref(resultNode.result)}, specDescription)
  233 + )
  234 + );
  235 + }
  236 + }
  237 + }
  238 +
  239 + if (failures.length) {
  240 + alert.appendChild(
  241 + createDom('span', {className: 'menu bar spec-list'},
  242 + createDom('span', {}, 'Spec List | '),
  243 + createDom('a', {className: 'failures-menu', href: '#'}, 'Failures')));
  244 + alert.appendChild(
  245 + createDom('span', {className: 'menu bar failure-list'},
  246 + createDom('a', {className: 'spec-list-menu', href: '#'}, 'Spec List'),
  247 + createDom('span', {}, ' | Failures ')));
  248 +
  249 + find('.failures-menu').onclick = function() {
  250 + setMenuModeTo('failure-list');
  251 + };
  252 + find('.spec-list-menu').onclick = function() {
  253 + setMenuModeTo('spec-list');
  254 + };
  255 +
  256 + setMenuModeTo('failure-list');
  257 +
  258 + var failureNode = find('.failures');
  259 + for (var i = 0; i < failures.length; i++) {
  260 + failureNode.appendChild(failures[i]);
  261 + }
  262 + }
  263 + };
  264 +
  265 + return this;
  266 +
  267 + function find(selector) {
  268 + return getContainer().querySelector('.jasmine_html-reporter ' + selector);
  269 + }
  270 +
  271 + function clearPrior() {
  272 + // return the reporter
  273 + var oldReporter = find('');
  274 +
  275 + if(oldReporter) {
  276 + getContainer().removeChild(oldReporter);
  277 + }
  278 + }
  279 +
  280 + function createDom(type, attrs, childrenVarArgs) {
  281 + var el = createElement(type);
  282 +
  283 + for (var i = 2; i < arguments.length; i++) {
  284 + var child = arguments[i];
  285 +
  286 + if (typeof child === 'string') {
  287 + el.appendChild(createTextNode(child));
  288 + } else {
  289 + if (child) {
  290 + el.appendChild(child);
  291 + }
  292 + }
  293 + }
  294 +
  295 + for (var attr in attrs) {
  296 + if (attr == 'className') {
  297 + el[attr] = attrs[attr];
  298 + } else {
  299 + el.setAttribute(attr, attrs[attr]);
  300 + }
  301 + }
  302 +
  303 + return el;
  304 + }
  305 +
  306 + function pluralize(singular, count) {
  307 + var word = (count == 1 ? singular : singular + 's');
  308 +
  309 + return '' + count + ' ' + word;
  310 + }
  311 +
  312 + function specHref(result) {
  313 + return addToExistingQueryString('spec', result.fullName);
  314 + }
  315 +
  316 + function defaultQueryString(key, value) {
  317 + return '?' + key + '=' + value;
  318 + }
  319 +
  320 + function setMenuModeTo(mode) {
  321 + htmlReporterMain.setAttribute('class', 'jasmine_html-reporter ' + mode);
  322 + }
  323 +
  324 + function noExpectations(result) {
  325 + return (result.failedExpectations.length + result.passedExpectations.length) === 0 &&
  326 + result.status === 'passed';
  327 + }
  328 + }
  329 +
  330 + return HtmlReporter;
  331 +};
  332 +
  333 +jasmineRequire.HtmlSpecFilter = function() {
  334 + function HtmlSpecFilter(options) {
  335 + var filterString = options && options.filterString() && options.filterString().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
  336 + var filterPattern = new RegExp(filterString);
  337 +
  338 + this.matches = function(specName) {
  339 + return filterPattern.test(specName);
  340 + };
  341 + }
  342 +
  343 + return HtmlSpecFilter;
  344 +};
  345 +
  346 +jasmineRequire.ResultsNode = function() {
  347 + function ResultsNode(result, type, parent) {
  348 + this.result = result;
  349 + this.type = type;
  350 + this.parent = parent;
  351 +
  352 + this.children = [];
  353 +
  354 + this.addChild = function(result, type) {
  355 + this.children.push(new ResultsNode(result, type, this));
  356 + };
  357 +
  358 + this.last = function() {
  359 + return this.children[this.children.length - 1];
  360 + };
  361 + }
  362 +
  363 + return ResultsNode;
  364 +};
  365 +
  366 +jasmineRequire.QueryString = function() {
  367 + function QueryString(options) {
  368 +
  369 + this.navigateWithNewParam = function(key, value) {
  370 + options.getWindowLocation().search = this.fullStringWithNewParam(key, value);
  371 + };
  372 +
  373 + this.fullStringWithNewParam = function(key, value) {
  374 + var paramMap = queryStringToParamMap();
  375 + paramMap[key] = value;
  376 + return toQueryString(paramMap);
  377 + };
  378 +
  379 + this.getParam = function(key) {
  380 + return queryStringToParamMap()[key];
  381 + };
  382 +
  383 + return this;
  384 +
  385 + function toQueryString(paramMap) {
  386 + var qStrPairs = [];
  387 + for (var prop in paramMap) {
  388 + qStrPairs.push(encodeURIComponent(prop) + '=' + encodeURIComponent(paramMap[prop]));
  389 + }
  390 + return '?' + qStrPairs.join('&');
  391 + }
  392 +
  393 + function queryStringToParamMap() {
  394 + var paramStr = options.getWindowLocation().search.substring(1),
  395 + params = [],
  396 + paramMap = {};
  397 +
  398 + if (paramStr.length > 0) {
  399 + params = paramStr.split('&');
  400 + for (var i = 0; i < params.length; i++) {
  401 + var p = params[i].split('=');
  402 + var value = decodeURIComponent(p[1]);
  403 + if (value === 'true' || value === 'false') {
  404 + value = JSON.parse(value);
  405 + }
  406 + paramMap[decodeURIComponent(p[0])] = value;
  407 + }
  408 + }
  409 +
  410 + return paramMap;
  411 + }
  412 +
  413 + }
  414 +
  415 + return QueryString;
  416 +};
... ...
test/lib/jasmine-2.2.0/jasmine.css 0 → 100644
... ... @@ -0,0 +1,62 @@
  1 +body { overflow-y: scroll; }
  2 +
  3 +.jasmine_html-reporter { background-color: #eee; padding: 5px; margin: -8px; font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333; }
  4 +.jasmine_html-reporter a { text-decoration: none; }
  5 +.jasmine_html-reporter a:hover { text-decoration: underline; }
  6 +.jasmine_html-reporter p, .jasmine_html-reporter h1, .jasmine_html-reporter h2, .jasmine_html-reporter h3, .jasmine_html-reporter h4, .jasmine_html-reporter h5, .jasmine_html-reporter h6 { margin: 0; line-height: 14px; }
  7 +.jasmine_html-reporter .banner, .jasmine_html-reporter .symbol-summary, .jasmine_html-reporter .summary, .jasmine_html-reporter .result-message, .jasmine_html-reporter .spec .description, .jasmine_html-reporter .spec-detail .description, .jasmine_html-reporter .alert .bar, .jasmine_html-reporter .stack-trace { padding-left: 9px; padding-right: 9px; }
  8 +.jasmine_html-reporter .banner { position: relative; }
  9 +.jasmine_html-reporter .banner .title { background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFoAAAAZCAMAAACGusnyAAACdlBMVEX/////AP+AgICqVaqAQICZM5mAVYCSSZKAQICOOY6ATYCLRouAQICJO4mSSYCIRIiPQICHPIeOR4CGQ4aMQICGPYaLRoCFQ4WKQICPPYWJRYCOQoSJQICNPoSIRICMQoSHQICHRICKQoOHQICKPoOJO4OJQYOMQICMQ4CIQYKLQICIPoKLQ4CKQICNPoKJQISMQ4KJQoSLQYKJQISLQ4KIQoSKQYKIQICIQISMQoSKQYKLQIOLQoOJQYGLQIOKQIOMQoGKQYOLQYGKQIOLQoGJQYOJQIOKQYGJQIOKQoGKQIGLQIKLQ4KKQoGLQYKJQIGKQYKJQIGKQIKJQoGKQYKLQIGKQYKLQIOJQoKKQoOJQYKKQIOJQoKKQoOKQIOLQoKKQYOLQYKJQIOKQoKKQYKKQoKJQYOKQYKLQIOKQoKLQYOKQYKLQIOJQoGKQYKJQYGJQoGKQYKLQoGLQYGKQoGJQYKKQYGJQIKKQoGJQYKLQIKKQYGLQYKKQYGKQYGKQYKJQYOKQoKJQYOKQYKLQYOLQYOKQYKLQYOKQoKKQYKKQYOKQYOJQYKKQYKLQYKKQIKKQoKKQYKKQYKKQoKJQIKKQYKLQYKKQYKKQIKKQYKKQYKKQYKKQIKKQYKJQYGLQYGKQYKKQYKKQYGKQIKKQYGKQYOJQoKKQYOLQYKKQYOKQoKKQYKKQoKKQYKKQYKJQYKLQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKJQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKLQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKmIDpEAAAA0XRSTlMAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyAiIyQlJycoKissLS4wMTQ1Njc4OTo7PDw+P0BCQ0RISUpLTE1OUFNUVVdYWFlaW15fYGFiY2ZnaGlqa2xtb3BxcnN0dnh5ent8fX5/gIGChIWIioyNjo+QkZOUlZaYmZqbnJ2eoKGio6WmqKmsra6vsLGztre4ubq7vL2+wMHDxMjJysvNzs/Q0dLU1tfY2dvc3t/g4eLj5ebn6Onq6+zt7u/w8vP09fb3+Pn6+/z9/vkVQXAAAAMaSURBVHhe5dXxV1N1GMfxz2ABbDgIAm5VDJOyVDIJLUMaVpBWUZUaGbmqoGpZRSiGiRWp6KoZ5AB0ZY50RImZQIlahKkMYXv/R90dBvET/rJfOr3Ouc8v99zPec59zvf56j+vYKlViSf7250X4Mr3O29Tgq08BdGB4DhcekEJ5YkQKFsgWZdtj9JpV+I8xPjLFqkrsEIqO8PHSpis36jWazcqjEsfJjkvRssVU37SdIOu4XCf5vEJPsnwJpnRNU9JmxhMk8l1gehIrq7hTFjzOD+Vf88629qKMJVNltInFeRexRQyJlNeqd1iGDlSzrIUIyXbyFfm3RYprcQRe7lqtWyGYbfc6dT0R2vmdOOkX3u55C1rP37ftiH+tDby4r/RBT0w8TyEkr+epB9XgPDmSYYWbrhCuFYaIyw3fDQAXTnSkh+ANofiHmWf9l+FY1I90FdQTetstO00o23novzVsJ7uB3/C5TkbjRwZ5JerwV4iRWq9HFbFMaK/d0TYqayRiQPuIxxS3Bu8JWU90/60tKi7vkhaznez0a/TbVOKj5CaOZh6fWG6/Lyv9B/ZLR1gw/S/fpbeVD3MCW1li6SvWDOn65tr99/uvWtBS0XDm4s1t+sOHpG0kpBKx/l77wOSnxLpcx6TXmXLTPQOKYOf9Q1dfr8/SJ2mFdCvl1Yl93DiHUZvXeLJbGSzYu5gVJ2slbSakOR8dxCq5adQ2oFLqsE9Ex3L4qQO0eOPeU5x56bypXp4onSEb5OkICX6lDat55TeoztNKQcJaakrz9KCb95oD69IKq+yKW4XPjknaS52V0TZqE2cTtXjcHSCRmUO88e+85hj3EP74i9p8pylw7lxgMDyyl6OV7ZejnjNMfatu87LxRbH0IS35gt2a4ZjmGpVBdKK3Wr6INk8jWWSGqbA55CKgjBRC6E9w78ydTg3ABS3AFV1QN0Y4Aa2pgEjWnQURj9L0ayK6R2ysEqxHUKzYnLvvyU+i9KM2JHJzE4vyZOyDcOwOsySajeLPc8sNvPJkFlyJd20wpqAzZeAfZ3oWybxd+P/3j+SG3uSBdf2VQAAAABJRU5ErkJggg==') no-repeat; background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgdmVyc2lvbj0iMS4xIgogICB3aWR0aD0iNjgxLjk2MjUyIgogICBoZWlnaHQ9IjE4Ny41IgogICBpZD0ic3ZnMiIKICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhOCI+PHJkZjpSREY+PGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPjxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PjxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz48L2NjOldvcms+PC9yZGY6UkRGPjwvbWV0YWRhdGE+PGRlZnMKICAgICBpZD0iZGVmczYiPjxjbGlwUGF0aAogICAgICAgaWQ9ImNsaXBQYXRoMTgiPjxwYXRoCiAgICAgICAgIGQ9Ik0gMCwxNTAwIDAsMCBsIDU0NTUuNzQsMCAwLDE1MDAgTCAwLDE1MDAgeiIKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgaWQ9InBhdGgyMCIgLz48L2NsaXBQYXRoPjwvZGVmcz48ZwogICAgIHRyYW5zZm9ybT0ibWF0cml4KDEuMjUsMCwwLC0xLjI1LDAsMTg3LjUpIgogICAgIGlkPSJnMTAiPjxnCiAgICAgICB0cmFuc2Zvcm09InNjYWxlKDAuMSwwLjEpIgogICAgICAgaWQ9ImcxMiI+PGcKICAgICAgICAgaWQ9ImcxNCI+PGcKICAgICAgICAgICBjbGlwLXBhdGg9InVybCgjY2xpcFBhdGgxOCkiCiAgICAgICAgICAgaWQ9ImcxNiI+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMTU0NCw1OTkuNDM0IGMgMC45MiwtNDAuMzUyIDI1LjY4LC04MS42MDIgNzEuNTMsLTgxLjYwMiAyNy41MSwwIDQ3LjY4LDEyLjgzMiA2MS40NCwzNS43NTQgMTIuODMsMjIuOTMgMTIuODMsNTYuODUyIDEyLjgzLDgyLjUyNyBsIDAsMzI5LjE4NCAtNzEuNTIsMCAwLDEwNC41NDMgMjY2LjgzLDAgMCwtMTA0LjU0MyAtNzAuNiwwIDAsLTM0NC43NyBjIDAsLTU4LjY5MSAtMy42OCwtMTA0LjUzMSAtNDQuOTMsLTE1Mi4yMTggLTM2LjY4LC00Mi4xOCAtOTYuMjgsLTY2LjAyIC0xNTMuMTQsLTY2LjAyIC0xMTcuMzcsMCAtMjA3LjI0LDc3Ljk0MSAtMjAyLjY0LDE5Ny4xNDUgbCAxMzAuMiwwIgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMjIiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDIzMDEuNCw2NjIuNjk1IGMgMCw4MC43MDMgLTY2Ljk0LDE0NS44MTMgLTE0Ny42MywxNDUuODEzIC04My40NCwwIC0xNDcuNjMsLTY4Ljc4MSAtMTQ3LjYzLC0xNTEuMzAxIDAsLTc5Ljc4NSA2Ni45NCwtMTQ1LjgwMSAxNDUuOCwtMTQ1LjgwMSA4NC4zNSwwIDE0OS40Niw2Ny44NTIgMTQ5LjQ2LDE1MS4yODkgeiBtIC0xLjgzLC0xODEuNTQ3IGMgLTM1Ljc3LC01NC4wOTcgLTkzLjUzLC03OC44NTkgLTE1Ny43MiwtNzguODU5IC0xNDAuMywwIC0yNTEuMjQsMTE2LjQ0OSAtMjUxLjI0LDI1NC45MTggMCwxNDIuMTI5IDExMy43LDI2MC40MSAyNTYuNzQsMjYwLjQxIDYzLjI3LDAgMTE4LjI5LC0yOS4zMzYgMTUyLjIyLC04Mi41MjMgbCAwLDY5LjY4NyAxNzUuMTQsMCAwLC0xMDQuNTI3IC02MS40NCwwIDAsLTI4MC41OTggNjEuNDQsMCAwLC0xMDQuNTI3IC0xNzUuMTQsMCAwLDY2LjAxOSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDI0IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSAyNjIyLjMzLDU1Ny4yNTggYyAzLjY3LC00NC4wMTYgMzMuMDEsLTczLjM0OCA3OC44NiwtNzMuMzQ4IDMzLjkzLDAgNjYuOTMsMjMuODI0IDY2LjkzLDYwLjUwNCAwLDQ4LjYwNiAtNDUuODQsNTYuODU2IC04My40NCw2Ni45NDEgLTg1LjI4LDIyLjAwNCAtMTc4LjgxLDQ4LjYwNiAtMTc4LjgxLDE1NS44NzkgMCw5My41MzYgNzguODYsMTQ3LjYzMyAxNjUuOTgsMTQ3LjYzMyA0NCwwIDgzLjQzLC05LjE3NiAxMTAuOTQsLTQ0LjAwOCBsIDAsMzMuOTIyIDgyLjUzLDAgMCwtMTMyLjk2NSAtMTA4LjIxLDAgYyAtMS44MywzNC44NTYgLTI4LjQyLDU3Ljc3NCAtNjMuMjYsNTcuNzc0IC0zMC4yNiwwIC02Mi4zNSwtMTcuNDIyIC02Mi4zNSwtNTEuMzQ4IDAsLTQ1Ljg0NyA0NC45MywtNTUuOTMgODAuNjksLTY0LjE4IDg4LjAyLC0yMC4xNzUgMTgyLjQ3LC00Ny42OTUgMTgyLjQ3LC0xNTcuNzM0IDAsLTk5LjAyNyAtODMuNDQsLTE1NC4wMzkgLTE3NS4xMywtMTU0LjAzOSAtNDkuNTMsMCAtOTQuNDYsMTUuNTgyIC0xMjYuNTUsNTMuMTggbCAwLC00MC4zNCAtODUuMjcsMCAwLDE0Mi4xMjkgMTE0LjYyLDAiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGgyNiIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMjk4OC4xOCw4MDAuMjU0IC02My4yNiwwIDAsMTA0LjUyNyAxNjUuMDUsMCAwLC03My4zNTUgYyAzMS4xOCw1MS4zNDcgNzguODYsODUuMjc3IDE0MS4yMSw4NS4yNzcgNjcuODUsMCAxMjQuNzEsLTQxLjI1OCAxNTIuMjEsLTEwMi42OTkgMjYuNiw2Mi4zNTEgOTIuNjIsMTAyLjY5OSAxNjAuNDcsMTAyLjY5OSA1My4xOSwwIDEwNS40NiwtMjIgMTQxLjIxLC02Mi4zNTEgMzguNTIsLTQ0LjkzOCAzOC41MiwtOTMuNTMyIDM4LjUyLC0xNDkuNDU3IGwgMCwtMTg1LjIzOSA2My4yNywwIDAsLTEwNC41MjcgLTIzOC40MiwwIDAsMTA0LjUyNyA2My4yOCwwIDAsMTU3LjcxNSBjIDAsMzIuMTAyIDAsNjAuNTI3IC0xNC42Nyw4OC45NTcgLTE4LjM0LDI2LjU4MiAtNDguNjEsNDAuMzQ0IC03OS43Nyw0MC4zNDQgLTMwLjI2LDAgLTYzLjI4LC0xMi44NDQgLTgyLjUzLC0zNi42NzIgLTIyLjkzLC0yOS4zNTUgLTIyLjkzLC01Ni44NjMgLTIyLjkzLC05Mi42MjkgbCAwLC0xNTcuNzE1IDYzLjI3LDAgMCwtMTA0LjUyNyAtMjM4LjQxLDAgMCwxMDQuNTI3IDYzLjI4LDAgMCwxNTAuMzgzIGMgMCwyOS4zNDggMCw2Ni4wMjMgLTE0LjY3LDkxLjY5OSAtMTUuNTksMjkuMzM2IC00Ny42OSw0NC45MzQgLTgwLjcsNDQuOTM0IC0zMS4xOCwwIC01Ny43NywtMTEuMDA4IC03Ny45NCwtMzUuNzc0IC0yNC43NywtMzAuMjUzIC0yNi42LC02Mi4zNDMgLTI2LjYsLTk5Ljk0MSBsIDAsLTE1MS4zMDEgNjMuMjcsMCAwLC0xMDQuNTI3IC0yMzguNCwwIDAsMTA0LjUyNyA2My4yNiwwIDAsMjgwLjU5OCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDI4IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSAzOTk4LjY2LDk1MS41NDcgLTExMS44NywwIDAsMTE4LjI5MyAxMTEuODcsMCAwLC0xMTguMjkzIHogbSAwLC00MzEuODkxIDYzLjI3LDAgMCwtMTA0LjUyNyAtMjM5LjMzLDAgMCwxMDQuNTI3IDY0LjE5LDAgMCwyODAuNTk4IC02My4yNywwIDAsMTA0LjUyNyAxNzUuMTQsMCAwLC0zODUuMTI1IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzAiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDQxNTkuMTIsODAwLjI1NCAtNjMuMjcsMCAwLDEwNC41MjcgMTc1LjE0LDAgMCwtNjkuNjg3IGMgMjkuMzUsNTQuMTAxIDg0LjM2LDgwLjY5OSAxNDQuODcsODAuNjk5IDUzLjE5LDAgMTA1LjQ1LC0yMi4wMTYgMTQxLjIyLC02MC41MjcgNDAuMzQsLTQ0LjkzNCA0MS4yNiwtODguMDMyIDQxLjI2LC0xNDMuOTU3IGwgMCwtMTkxLjY1MyA2My4yNywwIDAsLTEwNC41MjcgLTIzOC40LDAgMCwxMDQuNTI3IDYzLjI2LDAgMCwxNTguNjM3IGMgMCwzMC4yNjIgMCw2MS40MzQgLTE5LjI2LDg4LjAzNSAtMjAuMTcsMjYuNTgyIC01My4xOCwzOS40MTQgLTg2LjE5LDM5LjQxNCAtMzMuOTMsMCAtNjguNzcsLTEzLjc1IC04OC45NCwtNDEuMjUgLTIxLjA5LC0yNy41IC0yMS4wOSwtNjkuNjg3IC0yMS4wOSwtMTAyLjcwNyBsIDAsLTE0Mi4xMjkgNjMuMjYsMCAwLC0xMDQuNTI3IC0yMzguNCwwIDAsMTA0LjUyNyA2My4yNywwIDAsMjgwLjU5OCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDMyIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA1MDgyLjQ4LDcwMy45NjUgYyAtMTkuMjQsNzAuNjA1IC04MS42LDExNS41NDcgLTE1NC4wNCwxMTUuNTQ3IC02Ni4wNCwwIC0xMjkuMywtNTEuMzQ4IC0xNDMuMDUsLTExNS41NDcgbCAyOTcuMDksMCB6IG0gODUuMjcsLTE0NC44ODMgYyAtMzguNTEsLTkzLjUyMyAtMTI5LjI3LC0xNTYuNzkzIC0yMzEuMDUsLTE1Ni43OTMgLTE0My4wNywwIC0yNTcuNjgsMTExLjg3MSAtMjU3LjY4LDI1NS44MzYgMCwxNDQuODgzIDEwOS4xMiwyNjEuMzI4IDI1NC45MSwyNjEuMzI4IDY3Ljg3LDAgMTM1LjcyLC0zMC4yNTggMTgzLjM5LC03OC44NjMgNDguNjIsLTUxLjM0NCA2OC43OSwtMTEzLjY5NSA2OC43OSwtMTgzLjM4MyBsIC0zLjY3LC0zOS40MzQgLTM5Ni4xMywwIGMgMTQuNjcsLTY3Ljg2MyA3Ny4wMywtMTE3LjM2MyAxNDYuNzIsLTExNy4zNjMgNDguNTksMCA5MC43NiwxOC4zMjggMTE4LjI4LDU4LjY3MiBsIDExNi40NCwwIgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzQiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDY5MC44OTUsODUwLjcwMyA5MC43NSwwIDIyLjU0MywzMS4wMzUgMCwyNDMuMTIyIC0xMzUuODI5LDAgMCwtMjQzLjE0MSAyMi41MzYsLTMxLjAxNiIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDM2IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA2MzIuMzk1LDc0Mi4yNTggMjguMDM5LDg2LjMwNCAtMjIuNTUxLDMxLjA0IC0yMzEuMjIzLDc1LjEyOCAtNDEuOTc2LC0xMjkuMTgzIDIzMS4yNTcsLTc1LjEzNyAzNi40NTQsMTEuODQ4IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzgiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDcxNy40NDksNjUzLjEwNSAtNzMuNDEsNTMuMzYgLTM2LjQ4OCwtMTEuODc1IC0xNDIuOTAzLC0xOTYuNjkyIDEwOS44ODMsLTc5LjgyOCAxNDIuOTE4LDE5Ni43MDMgMCwzOC4zMzIiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGg0MCIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gODI4LjUyLDcwNi40NjUgLTczLjQyNiwtNTMuMzQgMC4wMTEsLTM4LjM1OSBMIDg5OC4wMDQsNDE4LjA3IDEwMDcuOSw0OTcuODk4IDg2NC45NzMsNjk0LjYwOSA4MjguNTIsNzA2LjQ2NSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDQyIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA4MTIuMDg2LDgyOC41ODYgMjguMDU1LC04Ni4zMiAzNi40ODQsLTExLjgzNiAyMzEuMjI1LDc1LjExNyAtNDEuOTcsMTI5LjE4MyAtMjMxLjIzOSwtNzUuMTQgLTIyLjU1NSwtMzEuMDA0IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNDQiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDczNi4zMDEsMTMzNS44OCBjIC0zMjMuMDQ3LDAgLTU4NS44NzUsLTI2Mi43OCAtNTg1Ljg3NSwtNTg1Ljc4MiAwLC0zMjMuMTE4IDI2Mi44MjgsLTU4NS45NzcgNTg1Ljg3NSwtNTg1Ljk3NyAzMjMuMDE5LDAgNTg1LjgwOSwyNjIuODU5IDU4NS44MDksNTg1Ljk3NyAwLDMyMy4wMDIgLTI2Mi43OSw1ODUuNzgyIC01ODUuODA5LDU4NS43ODIgbCAwLDAgeiBtIDAsLTExOC42MSBjIDI1Ny45NzIsMCA0NjcuMTg5LC0yMDkuMTMgNDY3LjE4OSwtNDY3LjE3MiAwLC0yNTguMTI5IC0yMDkuMjE3LC00NjcuMzQ4IC00NjcuMTg5LC00NjcuMzQ4IC0yNTguMDc0LDAgLTQ2Ny4yNTQsMjA5LjIxOSAtNDY3LjI1NCw0NjcuMzQ4IDAsMjU4LjA0MiAyMDkuMTgsNDY3LjE3MiA0NjcuMjU0LDQ2Ny4xNzIiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGg0NiIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMTA5MS4xMyw2MTkuODgzIC0xNzUuNzcxLDU3LjEyMSAxMS42MjksMzUuODA4IDE3NS43NjIsLTU3LjEyMSAtMTEuNjIsLTM1LjgwOCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDQ4IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0iTSA4NjYuOTU3LDkwMi4wNzQgODM2LjUsOTI0LjE5OSA5NDUuMTIxLDEwNzMuNzMgOTc1LjU4NiwxMDUxLjYxIDg2Ni45NTcsOTAyLjA3NCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDUwIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0iTSA2MDcuNDY1LDkwMy40NDUgNDk4Ljg1NSwxMDUyLjk3IDUyOS4zMiwxMDc1LjEgNjM3LjkzLDkyNS41NjYgNjA3LjQ2NSw5MDMuNDQ1IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNTIiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDM4MC42ODgsNjIyLjEyOSAtMTEuNjI2LDM1LjgwMSAxNzUuNzU4LDU3LjA5IDExLjYyMSwtMzUuODAxIC0xNzUuNzUzLC01Ny4wOSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDU0IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA3MTYuMjg5LDM3Ni41OSAzNy42NDA2LDAgMCwxODQuODE2IC0zNy42NDA2LDAgMCwtMTg0LjgxNiB6IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNTYiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjwvZz48L2c+PC9nPjwvZz48L3N2Zz4=') no-repeat, none; -moz-background-size: 100%; -o-background-size: 100%; -webkit-background-size: 100%; background-size: 100%; display: block; float: left; width: 90px; height: 25px; }
  10 +.jasmine_html-reporter .banner .version { margin-left: 14px; position: relative; top: 6px; }
  11 +.jasmine_html-reporter .banner .duration { position: absolute; right: 14px; top: 6px; }
  12 +.jasmine_html-reporter #jasmine_content { position: fixed; right: 100%; }
  13 +.jasmine_html-reporter .version { color: #aaa; }
  14 +.jasmine_html-reporter .banner { margin-top: 14px; }
  15 +.jasmine_html-reporter .duration { color: #aaa; float: right; }
  16 +.jasmine_html-reporter .symbol-summary { overflow: hidden; *zoom: 1; margin: 14px 0; }
  17 +.jasmine_html-reporter .symbol-summary li { display: inline-block; height: 8px; width: 14px; font-size: 16px; }
  18 +.jasmine_html-reporter .symbol-summary li.passed { font-size: 14px; }
  19 +.jasmine_html-reporter .symbol-summary li.passed:before { color: #007069; content: "\02022"; }
  20 +.jasmine_html-reporter .symbol-summary li.failed { line-height: 9px; }
  21 +.jasmine_html-reporter .symbol-summary li.failed:before { color: #ca3a11; content: "\d7"; font-weight: bold; margin-left: -1px; }
  22 +.jasmine_html-reporter .symbol-summary li.disabled { font-size: 14px; }
  23 +.jasmine_html-reporter .symbol-summary li.disabled:before { color: #bababa; content: "\02022"; }
  24 +.jasmine_html-reporter .symbol-summary li.pending { line-height: 17px; }
  25 +.jasmine_html-reporter .symbol-summary li.pending:before { color: #ba9d37; content: "*"; }
  26 +.jasmine_html-reporter .symbol-summary li.empty { font-size: 14px; }
  27 +.jasmine_html-reporter .symbol-summary li.empty:before { color: #ba9d37; content: "\02022"; }
  28 +.jasmine_html-reporter .exceptions { color: #fff; float: right; margin-top: 5px; margin-right: 5px; }
  29 +.jasmine_html-reporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; }
  30 +.jasmine_html-reporter .bar.failed { background-color: #ca3a11; }
  31 +.jasmine_html-reporter .bar.passed { background-color: #007069; }
  32 +.jasmine_html-reporter .bar.skipped { background-color: #bababa; }
  33 +.jasmine_html-reporter .bar.errored { background-color: #ca3a11; }
  34 +.jasmine_html-reporter .bar.menu { background-color: #fff; color: #aaa; }
  35 +.jasmine_html-reporter .bar.menu a { color: #333; }
  36 +.jasmine_html-reporter .bar a { color: white; }
  37 +.jasmine_html-reporter.spec-list .bar.menu.failure-list, .jasmine_html-reporter.spec-list .results .failures { display: none; }
  38 +.jasmine_html-reporter.failure-list .bar.menu.spec-list, .jasmine_html-reporter.failure-list .summary { display: none; }
  39 +.jasmine_html-reporter .running-alert { background-color: #666; }
  40 +.jasmine_html-reporter .results { margin-top: 14px; }
  41 +.jasmine_html-reporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; }
  42 +.jasmine_html-reporter.showDetails .summaryMenuItem:hover { text-decoration: underline; }
  43 +.jasmine_html-reporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; }
  44 +.jasmine_html-reporter.showDetails .summary { display: none; }
  45 +.jasmine_html-reporter.showDetails #details { display: block; }
  46 +.jasmine_html-reporter .summaryMenuItem { font-weight: bold; text-decoration: underline; }
  47 +.jasmine_html-reporter .summary { margin-top: 14px; }
  48 +.jasmine_html-reporter .summary ul { list-style-type: none; margin-left: 14px; padding-top: 0; padding-left: 0; }
  49 +.jasmine_html-reporter .summary ul.suite { margin-top: 7px; margin-bottom: 7px; }
  50 +.jasmine_html-reporter .summary li.passed a { color: #007069; }
  51 +.jasmine_html-reporter .summary li.failed a { color: #ca3a11; }
  52 +.jasmine_html-reporter .summary li.empty a { color: #ba9d37; }
  53 +.jasmine_html-reporter .summary li.pending a { color: #ba9d37; }
  54 +.jasmine_html-reporter .description + .suite { margin-top: 0; }
  55 +.jasmine_html-reporter .suite { margin-top: 14px; }
  56 +.jasmine_html-reporter .suite a { color: #333; }
  57 +.jasmine_html-reporter .failures .spec-detail { margin-bottom: 28px; }
  58 +.jasmine_html-reporter .failures .spec-detail .description { background-color: #ca3a11; }
  59 +.jasmine_html-reporter .failures .spec-detail .description a { color: white; }
  60 +.jasmine_html-reporter .result-message { padding-top: 14px; color: #333; white-space: pre; }
  61 +.jasmine_html-reporter .result-message span.result { display: block; }
  62 +.jasmine_html-reporter .stack-trace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666; border: 1px solid #ddd; background: white; white-space: pre; }
... ...
test/lib/jasmine-2.2.0/jasmine.js 0 → 100644
... ... @@ -0,0 +1,3048 @@
  1 +/*
  2 +Copyright (c) 2008-2015 Pivotal Labs
  3 +
  4 +Permission is hereby granted, free of charge, to any person obtaining
  5 +a copy of this software and associated documentation files (the
  6 +"Software"), to deal in the Software without restriction, including
  7 +without limitation the rights to use, copy, modify, merge, publish,
  8 +distribute, sublicense, and/or sell copies of the Software, and to
  9 +permit persons to whom the Software is furnished to do so, subject to
  10 +the following conditions:
  11 +
  12 +The above copyright notice and this permission notice shall be
  13 +included in all copies or substantial portions of the Software.
  14 +
  15 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  16 +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  17 +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  18 +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  19 +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  20 +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  21 +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  22 +*/
  23 +var getJasmineRequireObj = (function (jasmineGlobal) {
  24 + var jasmineRequire;
  25 +
  26 + if (typeof module !== 'undefined' && module.exports) {
  27 + jasmineGlobal = global;
  28 + jasmineRequire = exports;
  29 + } else {
  30 + if (typeof window !== 'undefined' && typeof window.toString === 'function' && window.toString() === '[object GjsGlobal]') {
  31 + jasmineGlobal = window;
  32 + }
  33 + jasmineRequire = jasmineGlobal.jasmineRequire = jasmineGlobal.jasmineRequire || {};
  34 + }
  35 +
  36 + function getJasmineRequire() {
  37 + return jasmineRequire;
  38 + }
  39 +
  40 + getJasmineRequire().core = function(jRequire) {
  41 + var j$ = {};
  42 +
  43 + jRequire.base(j$, jasmineGlobal);
  44 + j$.util = jRequire.util();
  45 + j$.Any = jRequire.Any();
  46 + j$.Anything = jRequire.Anything(j$);
  47 + j$.CallTracker = jRequire.CallTracker();
  48 + j$.MockDate = jRequire.MockDate();
  49 + j$.Clock = jRequire.Clock();
  50 + j$.DelayedFunctionScheduler = jRequire.DelayedFunctionScheduler();
  51 + j$.Env = jRequire.Env(j$);
  52 + j$.ExceptionFormatter = jRequire.ExceptionFormatter();
  53 + j$.Expectation = jRequire.Expectation();
  54 + j$.buildExpectationResult = jRequire.buildExpectationResult();
  55 + j$.JsApiReporter = jRequire.JsApiReporter();
  56 + j$.matchersUtil = jRequire.matchersUtil(j$);
  57 + j$.ObjectContaining = jRequire.ObjectContaining(j$);
  58 + j$.ArrayContaining = jRequire.ArrayContaining(j$);
  59 + j$.pp = jRequire.pp(j$);
  60 + j$.QueueRunner = jRequire.QueueRunner(j$);
  61 + j$.ReportDispatcher = jRequire.ReportDispatcher();
  62 + j$.Spec = jRequire.Spec(j$);
  63 + j$.SpyRegistry = jRequire.SpyRegistry(j$);
  64 + j$.SpyStrategy = jRequire.SpyStrategy();
  65 + j$.StringMatching = jRequire.StringMatching(j$);
  66 + j$.Suite = jRequire.Suite();
  67 + j$.Timer = jRequire.Timer();
  68 + j$.version = jRequire.version();
  69 +
  70 + j$.matchers = jRequire.requireMatchers(jRequire, j$);
  71 +
  72 + return j$;
  73 + };
  74 +
  75 + return getJasmineRequire;
  76 +})(this);
  77 +
  78 +getJasmineRequireObj().requireMatchers = function(jRequire, j$) {
  79 + var availableMatchers = [
  80 + 'toBe',
  81 + 'toBeCloseTo',
  82 + 'toBeDefined',
  83 + 'toBeFalsy',
  84 + 'toBeGreaterThan',
  85 + 'toBeLessThan',
  86 + 'toBeNaN',
  87 + 'toBeNull',
  88 + 'toBeTruthy',
  89 + 'toBeUndefined',
  90 + 'toContain',
  91 + 'toEqual',
  92 + 'toHaveBeenCalled',
  93 + 'toHaveBeenCalledWith',
  94 + 'toMatch',
  95 + 'toThrow',
  96 + 'toThrowError'
  97 + ],
  98 + matchers = {};
  99 +
  100 + for (var i = 0; i < availableMatchers.length; i++) {
  101 + var name = availableMatchers[i];
  102 + matchers[name] = jRequire[name](j$);
  103 + }
  104 +
  105 + return matchers;
  106 +};
  107 +
  108 +getJasmineRequireObj().base = function(j$, jasmineGlobal) {
  109 + j$.unimplementedMethod_ = function() {
  110 + throw new Error('unimplemented method');
  111 + };
  112 +
  113 + j$.MAX_PRETTY_PRINT_DEPTH = 40;
  114 + j$.MAX_PRETTY_PRINT_ARRAY_LENGTH = 100;
  115 + j$.DEFAULT_TIMEOUT_INTERVAL = 5000;
  116 +
  117 + j$.getGlobal = function() {
  118 + return jasmineGlobal;
  119 + };
  120 +
  121 + j$.getEnv = function(options) {
  122 + var env = j$.currentEnv_ = j$.currentEnv_ || new j$.Env(options);
  123 + //jasmine. singletons in here (setTimeout blah blah).
  124 + return env;
  125 + };
  126 +
  127 + j$.isArray_ = function(value) {
  128 + return j$.isA_('Array', value);
  129 + };
  130 +
  131 + j$.isString_ = function(value) {
  132 + return j$.isA_('String', value);
  133 + };
  134 +
  135 + j$.isNumber_ = function(value) {
  136 + return j$.isA_('Number', value);
  137 + };
  138 +
  139 + j$.isA_ = function(typeName, value) {
  140 + return Object.prototype.toString.apply(value) === '[object ' + typeName + ']';
  141 + };
  142 +
  143 + j$.isDomNode = function(obj) {
  144 + return obj.nodeType > 0;
  145 + };
  146 +
  147 + j$.fnNameFor = function(func) {
  148 + return func.name || func.toString().match(/^\s*function\s*(\w*)\s*\(/)[1];
  149 + };
  150 +
  151 + j$.any = function(clazz) {
  152 + return new j$.Any(clazz);
  153 + };
  154 +
  155 + j$.anything = function() {
  156 + return new j$.Anything();
  157 + };
  158 +
  159 + j$.objectContaining = function(sample) {
  160 + return new j$.ObjectContaining(sample);
  161 + };
  162 +
  163 + j$.stringMatching = function(expected) {
  164 + return new j$.StringMatching(expected);
  165 + };
  166 +
  167 + j$.arrayContaining = function(sample) {
  168 + return new j$.ArrayContaining(sample);
  169 + };
  170 +
  171 + j$.createSpy = function(name, originalFn) {
  172 +
  173 + var spyStrategy = new j$.SpyStrategy({
  174 + name: name,
  175 + fn: originalFn,
  176 + getSpy: function() { return spy; }
  177 + }),
  178 + callTracker = new j$.CallTracker(),
  179 + spy = function() {
  180 + var callData = {
  181 + object: this,
  182 + args: Array.prototype.slice.apply(arguments)
  183 + };
  184 +
  185 + callTracker.track(callData);
  186 + var returnValue = spyStrategy.exec.apply(this, arguments);
  187 + callData.returnValue = returnValue;
  188 +
  189 + return returnValue;
  190 + };
  191 +
  192 + for (var prop in originalFn) {
  193 + if (prop === 'and' || prop === 'calls') {
  194 + throw new Error('Jasmine spies would overwrite the \'and\' and \'calls\' properties on the object being spied upon');
  195 + }
  196 +
  197 + spy[prop] = originalFn[prop];
  198 + }
  199 +
  200 + spy.and = spyStrategy;
  201 + spy.calls = callTracker;
  202 +
  203 + return spy;
  204 + };
  205 +
  206 + j$.isSpy = function(putativeSpy) {
  207 + if (!putativeSpy) {
  208 + return false;
  209 + }
  210 + return putativeSpy.and instanceof j$.SpyStrategy &&
  211 + putativeSpy.calls instanceof j$.CallTracker;
  212 + };
  213 +
  214 + j$.createSpyObj = function(baseName, methodNames) {
  215 + if (j$.isArray_(baseName) && j$.util.isUndefined(methodNames)) {
  216 + methodNames = baseName;
  217 + baseName = 'unknown';
  218 + }
  219 +
  220 + if (!j$.isArray_(methodNames) || methodNames.length === 0) {
  221 + throw 'createSpyObj requires a non-empty array of method names to create spies for';
  222 + }
  223 + var obj = {};
  224 + for (var i = 0; i < methodNames.length; i++) {
  225 + obj[methodNames[i]] = j$.createSpy(baseName + '.' + methodNames[i]);
  226 + }
  227 + return obj;
  228 + };
  229 +};
  230 +
  231 +getJasmineRequireObj().util = function() {
  232 +
  233 + var util = {};
  234 +
  235 + util.inherit = function(childClass, parentClass) {
  236 + var Subclass = function() {
  237 + };
  238 + Subclass.prototype = parentClass.prototype;
  239 + childClass.prototype = new Subclass();
  240 + };
  241 +
  242 + util.htmlEscape = function(str) {
  243 + if (!str) {
  244 + return str;
  245 + }
  246 + return str.replace(/&/g, '&amp;')
  247 + .replace(/</g, '&lt;')
  248 + .replace(/>/g, '&gt;');
  249 + };
  250 +
  251 + util.argsToArray = function(args) {
  252 + var arrayOfArgs = [];
  253 + for (var i = 0; i < args.length; i++) {
  254 + arrayOfArgs.push(args[i]);
  255 + }
  256 + return arrayOfArgs;
  257 + };
  258 +
  259 + util.isUndefined = function(obj) {
  260 + return obj === void 0;
  261 + };
  262 +
  263 + util.arrayContains = function(array, search) {
  264 + var i = array.length;
  265 + while (i--) {
  266 + if (array[i] === search) {
  267 + return true;
  268 + }
  269 + }
  270 + return false;
  271 + };
  272 +
  273 + util.clone = function(obj) {
  274 + if (Object.prototype.toString.apply(obj) === '[object Array]') {
  275 + return obj.slice();
  276 + }
  277 +
  278 + var cloned = {};
  279 + for (var prop in obj) {
  280 + if (obj.hasOwnProperty(prop)) {
  281 + cloned[prop] = obj[prop];
  282 + }
  283 + }
  284 +
  285 + return cloned;
  286 + };
  287 +
  288 + return util;
  289 +};
  290 +
  291 +getJasmineRequireObj().Spec = function(j$) {
  292 + function Spec(attrs) {
  293 + this.expectationFactory = attrs.expectationFactory;
  294 + this.resultCallback = attrs.resultCallback || function() {};
  295 + this.id = attrs.id;
  296 + this.description = attrs.description || '';
  297 + this.queueableFn = attrs.queueableFn;
  298 + this.beforeAndAfterFns = attrs.beforeAndAfterFns || function() { return {befores: [], afters: []}; };
  299 + this.userContext = attrs.userContext || function() { return {}; };
  300 + this.onStart = attrs.onStart || function() {};
  301 + this.getSpecName = attrs.getSpecName || function() { return ''; };
  302 + this.expectationResultFactory = attrs.expectationResultFactory || function() { };
  303 + this.queueRunnerFactory = attrs.queueRunnerFactory || function() {};
  304 + this.catchingExceptions = attrs.catchingExceptions || function() { return true; };
  305 +
  306 + if (!this.queueableFn.fn) {
  307 + this.pend();
  308 + }
  309 +
  310 + this.result = {
  311 + id: this.id,
  312 + description: this.description,
  313 + fullName: this.getFullName(),
  314 + failedExpectations: [],
  315 + passedExpectations: [],
  316 + pendingReason: ''
  317 + };
  318 + }
  319 +
  320 + Spec.prototype.addExpectationResult = function(passed, data) {
  321 + var expectationResult = this.expectationResultFactory(data);
  322 + if (passed) {
  323 + this.result.passedExpectations.push(expectationResult);
  324 + } else {
  325 + this.result.failedExpectations.push(expectationResult);
  326 + }
  327 + };
  328 +
  329 + Spec.prototype.expect = function(actual) {
  330 + return this.expectationFactory(actual, this);
  331 + };
  332 +
  333 + Spec.prototype.execute = function(onComplete) {
  334 + var self = this;
  335 +
  336 + this.onStart(this);
  337 +
  338 + if (this.markedPending || this.disabled) {
  339 + complete();
  340 + return;
  341 + }
  342 +
  343 + var fns = this.beforeAndAfterFns();
  344 + var allFns = fns.befores.concat(this.queueableFn).concat(fns.afters);
  345 +
  346 + this.queueRunnerFactory({
  347 + queueableFns: allFns,
  348 + onException: function() { self.onException.apply(self, arguments); },
  349 + onComplete: complete,
  350 + userContext: this.userContext()
  351 + });
  352 +
  353 + function complete() {
  354 + self.result.status = self.status();
  355 + self.resultCallback(self.result);
  356 +
  357 + if (onComplete) {
  358 + onComplete();
  359 + }
  360 + }
  361 + };
  362 +
  363 + Spec.prototype.onException = function onException(e) {
  364 + if (Spec.isPendingSpecException(e)) {
  365 + this.pend(extractCustomPendingMessage(e));
  366 + return;
  367 + }
  368 +
  369 + this.addExpectationResult(false, {
  370 + matcherName: '',
  371 + passed: false,
  372 + expected: '',
  373 + actual: '',
  374 + error: e
  375 + });
  376 + };
  377 +
  378 + Spec.prototype.disable = function() {
  379 + this.disabled = true;
  380 + };
  381 +
  382 + Spec.prototype.pend = function(message) {
  383 + this.markedPending = true;
  384 + if (message) {
  385 + this.result.pendingReason = message;
  386 + }
  387 + };
  388 +
  389 + Spec.prototype.status = function() {
  390 + if (this.disabled) {
  391 + return 'disabled';
  392 + }
  393 +
  394 + if (this.markedPending) {
  395 + return 'pending';
  396 + }
  397 +
  398 + if (this.result.failedExpectations.length > 0) {
  399 + return 'failed';
  400 + } else {
  401 + return 'passed';
  402 + }
  403 + };
  404 +
  405 + Spec.prototype.isExecutable = function() {
  406 + return !this.disabled && !this.markedPending;
  407 + };
  408 +
  409 + Spec.prototype.getFullName = function() {
  410 + return this.getSpecName(this);
  411 + };
  412 +
  413 + var extractCustomPendingMessage = function(e) {
  414 + var fullMessage = e.toString(),
  415 + boilerplateStart = fullMessage.indexOf(Spec.pendingSpecExceptionMessage),
  416 + boilerplateEnd = boilerplateStart + Spec.pendingSpecExceptionMessage.length;
  417 +
  418 + return fullMessage.substr(boilerplateEnd);
  419 + };
  420 +
  421 + Spec.pendingSpecExceptionMessage = '=> marked Pending';
  422 +
  423 + Spec.isPendingSpecException = function(e) {
  424 + return !!(e && e.toString && e.toString().indexOf(Spec.pendingSpecExceptionMessage) !== -1);
  425 + };
  426 +
  427 + return Spec;
  428 +};
  429 +
  430 +if (typeof window == void 0 && typeof exports == 'object') {
  431 + exports.Spec = jasmineRequire.Spec;
  432 +}
  433 +
  434 +getJasmineRequireObj().Env = function(j$) {
  435 + function Env(options) {
  436 + options = options || {};
  437 +
  438 + var self = this;
  439 + var global = options.global || j$.getGlobal();
  440 +
  441 + var totalSpecsDefined = 0;
  442 +
  443 + var catchExceptions = true;
  444 +
  445 + var realSetTimeout = j$.getGlobal().setTimeout;
  446 + var realClearTimeout = j$.getGlobal().clearTimeout;
  447 + this.clock = new j$.Clock(global, new j$.DelayedFunctionScheduler(), new j$.MockDate(global));
  448 +
  449 + var runnableLookupTable = {};
  450 + var runnableResources = {};
  451 +
  452 + var currentSpec = null;
  453 + var currentlyExecutingSuites = [];
  454 + var currentDeclarationSuite = null;
  455 +
  456 + var currentSuite = function() {
  457 + return currentlyExecutingSuites[currentlyExecutingSuites.length - 1];
  458 + };
  459 +
  460 + var currentRunnable = function() {
  461 + return currentSpec || currentSuite();
  462 + };
  463 +
  464 + var reporter = new j$.ReportDispatcher([
  465 + 'jasmineStarted',
  466 + 'jasmineDone',
  467 + 'suiteStarted',
  468 + 'suiteDone',
  469 + 'specStarted',
  470 + 'specDone'
  471 + ]);
  472 +
  473 + this.specFilter = function() {
  474 + return true;
  475 + };
  476 +
  477 + this.addCustomEqualityTester = function(tester) {
  478 + if(!currentRunnable()) {
  479 + throw new Error('Custom Equalities must be added in a before function or a spec');
  480 + }
  481 + runnableResources[currentRunnable().id].customEqualityTesters.push(tester);
  482 + };
  483 +
  484 + this.addMatchers = function(matchersToAdd) {
  485 + if(!currentRunnable()) {
  486 + throw new Error('Matchers must be added in a before function or a spec');
  487 + }
  488 + var customMatchers = runnableResources[currentRunnable().id].customMatchers;
  489 + for (var matcherName in matchersToAdd) {
  490 + customMatchers[matcherName] = matchersToAdd[matcherName];
  491 + }
  492 + };
  493 +
  494 + j$.Expectation.addCoreMatchers(j$.matchers);
  495 +
  496 + var nextSpecId = 0;
  497 + var getNextSpecId = function() {
  498 + return 'spec' + nextSpecId++;
  499 + };
  500 +
  501 + var nextSuiteId = 0;
  502 + var getNextSuiteId = function() {
  503 + return 'suite' + nextSuiteId++;
  504 + };
  505 +
  506 + var expectationFactory = function(actual, spec) {
  507 + return j$.Expectation.Factory({
  508 + util: j$.matchersUtil,
  509 + customEqualityTesters: runnableResources[spec.id].customEqualityTesters,
  510 + customMatchers: runnableResources[spec.id].customMatchers,
  511 + actual: actual,
  512 + addExpectationResult: addExpectationResult
  513 + });
  514 +
  515 + function addExpectationResult(passed, result) {
  516 + return spec.addExpectationResult(passed, result);
  517 + }
  518 + };
  519 +
  520 + var defaultResourcesForRunnable = function(id, parentRunnableId) {
  521 + var resources = {spies: [], customEqualityTesters: [], customMatchers: {}};
  522 +
  523 + if(runnableResources[parentRunnableId]){
  524 + resources.customEqualityTesters = j$.util.clone(runnableResources[parentRunnableId].customEqualityTesters);
  525 + resources.customMatchers = j$.util.clone(runnableResources[parentRunnableId].customMatchers);
  526 + }
  527 +
  528 + runnableResources[id] = resources;
  529 + };
  530 +
  531 + var clearResourcesForRunnable = function(id) {
  532 + spyRegistry.clearSpies();
  533 + delete runnableResources[id];
  534 + };
  535 +
  536 + var beforeAndAfterFns = function(suite, runnablesExplictlySet) {
  537 + return function() {
  538 + var befores = [],
  539 + afters = [],
  540 + beforeAlls = [],
  541 + afterAlls = [];
  542 +
  543 + while(suite) {
  544 + befores = befores.concat(suite.beforeFns);
  545 + afters = afters.concat(suite.afterFns);
  546 +
  547 + if (runnablesExplictlySet()) {
  548 + beforeAlls = beforeAlls.concat(suite.beforeAllFns);
  549 + afterAlls = afterAlls.concat(suite.afterAllFns);
  550 + }
  551 +
  552 + suite = suite.parentSuite;
  553 + }
  554 + return {
  555 + befores: beforeAlls.reverse().concat(befores.reverse()),
  556 + afters: afters.concat(afterAlls)
  557 + };
  558 + };
  559 + };
  560 +
  561 + var getSpecName = function(spec, suite) {
  562 + return suite.getFullName() + ' ' + spec.description;
  563 + };
  564 +
  565 + // TODO: we may just be able to pass in the fn instead of wrapping here
  566 + var buildExpectationResult = j$.buildExpectationResult,
  567 + exceptionFormatter = new j$.ExceptionFormatter(),
  568 + expectationResultFactory = function(attrs) {
  569 + attrs.messageFormatter = exceptionFormatter.message;
  570 + attrs.stackFormatter = exceptionFormatter.stack;
  571 +
  572 + return buildExpectationResult(attrs);
  573 + };
  574 +
  575 + // TODO: fix this naming, and here's where the value comes in
  576 + this.catchExceptions = function(value) {
  577 + catchExceptions = !!value;
  578 + return catchExceptions;
  579 + };
  580 +
  581 + this.catchingExceptions = function() {
  582 + return catchExceptions;
  583 + };
  584 +
  585 + var maximumSpecCallbackDepth = 20;
  586 + var currentSpecCallbackDepth = 0;
  587 +
  588 + function clearStack(fn) {
  589 + currentSpecCallbackDepth++;
  590 + if (currentSpecCallbackDepth >= maximumSpecCallbackDepth) {
  591 + currentSpecCallbackDepth = 0;
  592 + realSetTimeout(fn, 0);
  593 + } else {
  594 + fn();
  595 + }
  596 + }
  597 +
  598 + var catchException = function(e) {
  599 + return j$.Spec.isPendingSpecException(e) || catchExceptions;
  600 + };
  601 +
  602 + var queueRunnerFactory = function(options) {
  603 + options.catchException = catchException;
  604 + options.clearStack = options.clearStack || clearStack;
  605 + options.timer = {setTimeout: realSetTimeout, clearTimeout: realClearTimeout};
  606 + options.fail = self.fail;
  607 +
  608 + new j$.QueueRunner(options).execute();
  609 + };
  610 +
  611 + var topSuite = new j$.Suite({
  612 + env: this,
  613 + id: getNextSuiteId(),
  614 + description: 'Jasmine__TopLevel__Suite',
  615 + queueRunner: queueRunnerFactory
  616 + });
  617 + runnableLookupTable[topSuite.id] = topSuite;
  618 + defaultResourcesForRunnable(topSuite.id);
  619 + currentDeclarationSuite = topSuite;
  620 +
  621 + this.topSuite = function() {
  622 + return topSuite;
  623 + };
  624 +
  625 + this.execute = function(runnablesToRun) {
  626 + if(runnablesToRun) {
  627 + runnablesExplictlySet = true;
  628 + } else if (focusedRunnables.length) {
  629 + runnablesExplictlySet = true;
  630 + runnablesToRun = focusedRunnables;
  631 + } else {
  632 + runnablesToRun = [topSuite.id];
  633 + }
  634 +
  635 + var allFns = [];
  636 + for(var i = 0; i < runnablesToRun.length; i++) {
  637 + var runnable = runnableLookupTable[runnablesToRun[i]];
  638 + allFns.push((function(runnable) { return { fn: function(done) { runnable.execute(done); } }; })(runnable));
  639 + }
  640 +
  641 + reporter.jasmineStarted({
  642 + totalSpecsDefined: totalSpecsDefined
  643 + });
  644 +
  645 + queueRunnerFactory({queueableFns: allFns, onComplete: reporter.jasmineDone});
  646 + };
  647 +
  648 + this.addReporter = function(reporterToAdd) {
  649 + reporter.addReporter(reporterToAdd);
  650 + };
  651 +
  652 + var spyRegistry = new j$.SpyRegistry({currentSpies: function() {
  653 + if(!currentRunnable()) {
  654 + throw new Error('Spies must be created in a before function or a spec');
  655 + }
  656 + return runnableResources[currentRunnable().id].spies;
  657 + }});
  658 +
  659 + this.spyOn = function() {
  660 + return spyRegistry.spyOn.apply(spyRegistry, arguments);
  661 + };
  662 +
  663 + var suiteFactory = function(description) {
  664 + var suite = new j$.Suite({
  665 + env: self,
  666 + id: getNextSuiteId(),
  667 + description: description,
  668 + parentSuite: currentDeclarationSuite,
  669 + queueRunner: queueRunnerFactory,
  670 + onStart: suiteStarted,
  671 + expectationFactory: expectationFactory,
  672 + expectationResultFactory: expectationResultFactory,
  673 + runnablesExplictlySetGetter: runnablesExplictlySetGetter,
  674 + resultCallback: function(attrs) {
  675 + if (!suite.disabled) {
  676 + clearResourcesForRunnable(suite.id);
  677 + }
  678 + currentlyExecutingSuites.pop();
  679 + reporter.suiteDone(attrs);
  680 + }
  681 + });
  682 +
  683 + runnableLookupTable[suite.id] = suite;
  684 + return suite;
  685 +
  686 + function suiteStarted(suite) {
  687 + currentlyExecutingSuites.push(suite);
  688 + defaultResourcesForRunnable(suite.id, suite.parentSuite.id);
  689 + reporter.suiteStarted(suite.result);
  690 + }
  691 + };
  692 +
  693 + this.describe = function(description, specDefinitions) {
  694 + var suite = suiteFactory(description);
  695 + addSpecsToSuite(suite, specDefinitions);
  696 + return suite;
  697 + };
  698 +
  699 + this.xdescribe = function(description, specDefinitions) {
  700 + var suite = this.describe(description, specDefinitions);
  701 + suite.disable();
  702 + return suite;
  703 + };
  704 +
  705 + var focusedRunnables = [];
  706 +
  707 + this.fdescribe = function(description, specDefinitions) {
  708 + var suite = suiteFactory(description);
  709 + suite.isFocused = true;
  710 +
  711 + focusedRunnables.push(suite.id);
  712 + unfocusAncestor();
  713 + addSpecsToSuite(suite, specDefinitions);
  714 +
  715 + return suite;
  716 + };
  717 +
  718 + function addSpecsToSuite(suite, specDefinitions) {
  719 + var parentSuite = currentDeclarationSuite;
  720 + parentSuite.addChild(suite);
  721 + currentDeclarationSuite = suite;
  722 +
  723 + var declarationError = null;
  724 + try {
  725 + specDefinitions.call(suite);
  726 + } catch (e) {
  727 + declarationError = e;
  728 + }
  729 +
  730 + if (declarationError) {
  731 + self.it('encountered a declaration exception', function() {
  732 + throw declarationError;
  733 + });
  734 + }
  735 +
  736 + currentDeclarationSuite = parentSuite;
  737 + }
  738 +
  739 + function findFocusedAncestor(suite) {
  740 + while (suite) {
  741 + if (suite.isFocused) {
  742 + return suite.id;
  743 + }
  744 + suite = suite.parentSuite;
  745 + }
  746 +
  747 + return null;
  748 + }
  749 +
  750 + function unfocusAncestor() {
  751 + var focusedAncestor = findFocusedAncestor(currentDeclarationSuite);
  752 + if (focusedAncestor) {
  753 + for (var i = 0; i < focusedRunnables.length; i++) {
  754 + if (focusedRunnables[i] === focusedAncestor) {
  755 + focusedRunnables.splice(i, 1);
  756 + break;
  757 + }
  758 + }
  759 + }
  760 + }
  761 +
  762 + var runnablesExplictlySet = false;
  763 +
  764 + var runnablesExplictlySetGetter = function(){
  765 + return runnablesExplictlySet;
  766 + };
  767 +
  768 + var specFactory = function(description, fn, suite, timeout) {
  769 + totalSpecsDefined++;
  770 + var spec = new j$.Spec({
  771 + id: getNextSpecId(),
  772 + beforeAndAfterFns: beforeAndAfterFns(suite, runnablesExplictlySetGetter),
  773 + expectationFactory: expectationFactory,
  774 + resultCallback: specResultCallback,
  775 + getSpecName: function(spec) {
  776 + return getSpecName(spec, suite);
  777 + },
  778 + onStart: specStarted,
  779 + description: description,
  780 + expectationResultFactory: expectationResultFactory,
  781 + queueRunnerFactory: queueRunnerFactory,
  782 + userContext: function() { return suite.clonedSharedUserContext(); },
  783 + queueableFn: {
  784 + fn: fn,
  785 + timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; }
  786 + }
  787 + });
  788 +
  789 + runnableLookupTable[spec.id] = spec;
  790 +
  791 + if (!self.specFilter(spec)) {
  792 + spec.disable();
  793 + }
  794 +
  795 + return spec;
  796 +
  797 + function specResultCallback(result) {
  798 + clearResourcesForRunnable(spec.id);
  799 + currentSpec = null;
  800 + reporter.specDone(result);
  801 + }
  802 +
  803 + function specStarted(spec) {
  804 + currentSpec = spec;
  805 + defaultResourcesForRunnable(spec.id, suite.id);
  806 + reporter.specStarted(spec.result);
  807 + }
  808 + };
  809 +
  810 + this.it = function(description, fn, timeout) {
  811 + var spec = specFactory(description, fn, currentDeclarationSuite, timeout);
  812 + currentDeclarationSuite.addChild(spec);
  813 + return spec;
  814 + };
  815 +
  816 + this.xit = function() {
  817 + var spec = this.it.apply(this, arguments);
  818 + spec.pend();
  819 + return spec;
  820 + };
  821 +
  822 + this.fit = function(){
  823 + var spec = this.it.apply(this, arguments);
  824 +
  825 + focusedRunnables.push(spec.id);
  826 + unfocusAncestor();
  827 + return spec;
  828 + };
  829 +
  830 + this.expect = function(actual) {
  831 + if (!currentRunnable()) {
  832 + throw new Error('\'expect\' was used when there was no current spec, this could be because an asynchronous test timed out');
  833 + }
  834 +
  835 + return currentRunnable().expect(actual);
  836 + };
  837 +
  838 + this.beforeEach = function(beforeEachFunction, timeout) {
  839 + currentDeclarationSuite.beforeEach({
  840 + fn: beforeEachFunction,
  841 + timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; }
  842 + });
  843 + };
  844 +
  845 + this.beforeAll = function(beforeAllFunction, timeout) {
  846 + currentDeclarationSuite.beforeAll({
  847 + fn: beforeAllFunction,
  848 + timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; }
  849 + });
  850 + };
  851 +
  852 + this.afterEach = function(afterEachFunction, timeout) {
  853 + currentDeclarationSuite.afterEach({
  854 + fn: afterEachFunction,
  855 + timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; }
  856 + });
  857 + };
  858 +
  859 + this.afterAll = function(afterAllFunction, timeout) {
  860 + currentDeclarationSuite.afterAll({
  861 + fn: afterAllFunction,
  862 + timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; }
  863 + });
  864 + };
  865 +
  866 + this.pending = function(message) {
  867 + var fullMessage = j$.Spec.pendingSpecExceptionMessage;
  868 + if(message) {
  869 + fullMessage += message;
  870 + }
  871 + throw fullMessage;
  872 + };
  873 +
  874 + this.fail = function(error) {
  875 + var message = 'Failed';
  876 + if (error) {
  877 + message += ': ';
  878 + message += error.message || error;
  879 + }
  880 +
  881 + currentRunnable().addExpectationResult(false, {
  882 + matcherName: '',
  883 + passed: false,
  884 + expected: '',
  885 + actual: '',
  886 + message: message,
  887 + error: error && error.message ? error : null
  888 + });
  889 + };
  890 + }
  891 +
  892 + return Env;
  893 +};
  894 +
  895 +getJasmineRequireObj().JsApiReporter = function() {
  896 +
  897 + var noopTimer = {
  898 + start: function(){},
  899 + elapsed: function(){ return 0; }
  900 + };
  901 +
  902 + function JsApiReporter(options) {
  903 + var timer = options.timer || noopTimer,
  904 + status = 'loaded';
  905 +
  906 + this.started = false;
  907 + this.finished = false;
  908 +
  909 + this.jasmineStarted = function() {
  910 + this.started = true;
  911 + status = 'started';
  912 + timer.start();
  913 + };
  914 +
  915 + var executionTime;
  916 +
  917 + this.jasmineDone = function() {
  918 + this.finished = true;
  919 + executionTime = timer.elapsed();
  920 + status = 'done';
  921 + };
  922 +
  923 + this.status = function() {
  924 + return status;
  925 + };
  926 +
  927 + var suites = [],
  928 + suites_hash = {};
  929 +
  930 + this.suiteStarted = function(result) {
  931 + suites_hash[result.id] = result;
  932 + };
  933 +
  934 + this.suiteDone = function(result) {
  935 + storeSuite(result);
  936 + };
  937 +
  938 + this.suiteResults = function(index, length) {
  939 + return suites.slice(index, index + length);
  940 + };
  941 +
  942 + function storeSuite(result) {
  943 + suites.push(result);
  944 + suites_hash[result.id] = result;
  945 + }
  946 +
  947 + this.suites = function() {
  948 + return suites_hash;
  949 + };
  950 +
  951 + var specs = [];
  952 +
  953 + this.specDone = function(result) {
  954 + specs.push(result);
  955 + };
  956 +
  957 + this.specResults = function(index, length) {
  958 + return specs.slice(index, index + length);
  959 + };
  960 +
  961 + this.specs = function() {
  962 + return specs;
  963 + };
  964 +
  965 + this.executionTime = function() {
  966 + return executionTime;
  967 + };
  968 +
  969 + }
  970 +
  971 + return JsApiReporter;
  972 +};
  973 +
  974 +getJasmineRequireObj().CallTracker = function() {
  975 +
  976 + function CallTracker() {
  977 + var calls = [];
  978 +
  979 + this.track = function(context) {
  980 + calls.push(context);
  981 + };
  982 +
  983 + this.any = function() {
  984 + return !!calls.length;
  985 + };
  986 +
  987 + this.count = function() {
  988 + return calls.length;
  989 + };
  990 +
  991 + this.argsFor = function(index) {
  992 + var call = calls[index];
  993 + return call ? call.args : [];
  994 + };
  995 +
  996 + this.all = function() {
  997 + return calls;
  998 + };
  999 +
  1000 + this.allArgs = function() {
  1001 + var callArgs = [];
  1002 + for(var i = 0; i < calls.length; i++){
  1003 + callArgs.push(calls[i].args);
  1004 + }
  1005 +
  1006 + return callArgs;
  1007 + };
  1008 +
  1009 + this.first = function() {
  1010 + return calls[0];
  1011 + };
  1012 +
  1013 + this.mostRecent = function() {
  1014 + return calls[calls.length - 1];
  1015 + };
  1016 +
  1017 + this.reset = function() {
  1018 + calls = [];
  1019 + };
  1020 + }
  1021 +
  1022 + return CallTracker;
  1023 +};
  1024 +
  1025 +getJasmineRequireObj().Clock = function() {
  1026 + function Clock(global, delayedFunctionScheduler, mockDate) {
  1027 + var self = this,
  1028 + realTimingFunctions = {
  1029 + setTimeout: global.setTimeout,
  1030 + clearTimeout: global.clearTimeout,
  1031 + setInterval: global.setInterval,
  1032 + clearInterval: global.clearInterval
  1033 + },
  1034 + fakeTimingFunctions = {
  1035 + setTimeout: setTimeout,
  1036 + clearTimeout: clearTimeout,
  1037 + setInterval: setInterval,
  1038 + clearInterval: clearInterval
  1039 + },
  1040 + installed = false,
  1041 + timer;
  1042 +
  1043 +
  1044 + self.install = function() {
  1045 + replace(global, fakeTimingFunctions);
  1046 + timer = fakeTimingFunctions;
  1047 + installed = true;
  1048 +
  1049 + return self;
  1050 + };
  1051 +
  1052 + self.uninstall = function() {
  1053 + delayedFunctionScheduler.reset();
  1054 + mockDate.uninstall();
  1055 + replace(global, realTimingFunctions);
  1056 +
  1057 + timer = realTimingFunctions;
  1058 + installed = false;
  1059 + };
  1060 +
  1061 + self.mockDate = function(initialDate) {
  1062 + mockDate.install(initialDate);
  1063 + };
  1064 +
  1065 + self.setTimeout = function(fn, delay, params) {
  1066 + if (legacyIE()) {
  1067 + if (arguments.length > 2) {
  1068 + throw new Error('IE < 9 cannot support extra params to setTimeout without a polyfill');
  1069 + }
  1070 + return timer.setTimeout(fn, delay);
  1071 + }
  1072 + return Function.prototype.apply.apply(timer.setTimeout, [global, arguments]);
  1073 + };
  1074 +
  1075 + self.setInterval = function(fn, delay, params) {
  1076 + if (legacyIE()) {
  1077 + if (arguments.length > 2) {
  1078 + throw new Error('IE < 9 cannot support extra params to setInterval without a polyfill');
  1079 + }
  1080 + return timer.setInterval(fn, delay);
  1081 + }
  1082 + return Function.prototype.apply.apply(timer.setInterval, [global, arguments]);
  1083 + };
  1084 +
  1085 + self.clearTimeout = function(id) {
  1086 + return Function.prototype.call.apply(timer.clearTimeout, [global, id]);
  1087 + };
  1088 +
  1089 + self.clearInterval = function(id) {
  1090 + return Function.prototype.call.apply(timer.clearInterval, [global, id]);
  1091 + };
  1092 +
  1093 + self.tick = function(millis) {
  1094 + if (installed) {
  1095 + mockDate.tick(millis);
  1096 + delayedFunctionScheduler.tick(millis);
  1097 + } else {
  1098 + throw new Error('Mock clock is not installed, use jasmine.clock().install()');
  1099 + }
  1100 + };
  1101 +
  1102 + return self;
  1103 +
  1104 + function legacyIE() {
  1105 + //if these methods are polyfilled, apply will be present
  1106 + return !(realTimingFunctions.setTimeout || realTimingFunctions.setInterval).apply;
  1107 + }
  1108 +
  1109 + function replace(dest, source) {
  1110 + for (var prop in source) {
  1111 + dest[prop] = source[prop];
  1112 + }
  1113 + }
  1114 +
  1115 + function setTimeout(fn, delay) {
  1116 + return delayedFunctionScheduler.scheduleFunction(fn, delay, argSlice(arguments, 2));
  1117 + }
  1118 +
  1119 + function clearTimeout(id) {
  1120 + return delayedFunctionScheduler.removeFunctionWithId(id);
  1121 + }
  1122 +
  1123 + function setInterval(fn, interval) {
  1124 + return delayedFunctionScheduler.scheduleFunction(fn, interval, argSlice(arguments, 2), true);
  1125 + }
  1126 +
  1127 + function clearInterval(id) {
  1128 + return delayedFunctionScheduler.removeFunctionWithId(id);
  1129 + }
  1130 +
  1131 + function argSlice(argsObj, n) {
  1132 + return Array.prototype.slice.call(argsObj, n);
  1133 + }
  1134 + }
  1135 +
  1136 + return Clock;
  1137 +};
  1138 +
  1139 +getJasmineRequireObj().DelayedFunctionScheduler = function() {
  1140 + function DelayedFunctionScheduler() {
  1141 + var self = this;
  1142 + var scheduledLookup = [];
  1143 + var scheduledFunctions = {};
  1144 + var currentTime = 0;
  1145 + var delayedFnCount = 0;
  1146 +
  1147 + self.tick = function(millis) {
  1148 + millis = millis || 0;
  1149 + var endTime = currentTime + millis;
  1150 +
  1151 + runScheduledFunctions(endTime);
  1152 + currentTime = endTime;
  1153 + };
  1154 +
  1155 + self.scheduleFunction = function(funcToCall, millis, params, recurring, timeoutKey, runAtMillis) {
  1156 + var f;
  1157 + if (typeof(funcToCall) === 'string') {
  1158 + /* jshint evil: true */
  1159 + f = function() { return eval(funcToCall); };
  1160 + /* jshint evil: false */
  1161 + } else {
  1162 + f = funcToCall;
  1163 + }
  1164 +
  1165 + millis = millis || 0;
  1166 + timeoutKey = timeoutKey || ++delayedFnCount;
  1167 + runAtMillis = runAtMillis || (currentTime + millis);
  1168 +
  1169 + var funcToSchedule = {
  1170 + runAtMillis: runAtMillis,
  1171 + funcToCall: f,
  1172 + recurring: recurring,
  1173 + params: params,
  1174 + timeoutKey: timeoutKey,
  1175 + millis: millis
  1176 + };
  1177 +
  1178 + if (runAtMillis in scheduledFunctions) {
  1179 + scheduledFunctions[runAtMillis].push(funcToSchedule);
  1180 + } else {
  1181 + scheduledFunctions[runAtMillis] = [funcToSchedule];
  1182 + scheduledLookup.push(runAtMillis);
  1183 + scheduledLookup.sort(function (a, b) {
  1184 + return a - b;
  1185 + });
  1186 + }
  1187 +
  1188 + return timeoutKey;
  1189 + };
  1190 +
  1191 + self.removeFunctionWithId = function(timeoutKey) {
  1192 + for (var runAtMillis in scheduledFunctions) {
  1193 + var funcs = scheduledFunctions[runAtMillis];
  1194 + var i = indexOfFirstToPass(funcs, function (func) {
  1195 + return func.timeoutKey === timeoutKey;
  1196 + });
  1197 +
  1198 + if (i > -1) {
  1199 + if (funcs.length === 1) {
  1200 + delete scheduledFunctions[runAtMillis];
  1201 + deleteFromLookup(runAtMillis);
  1202 + } else {
  1203 + funcs.splice(i, 1);
  1204 + }
  1205 +
  1206 + // intervals get rescheduled when executed, so there's never more
  1207 + // than a single scheduled function with a given timeoutKey
  1208 + break;
  1209 + }
  1210 + }
  1211 + };
  1212 +
  1213 + self.reset = function() {
  1214 + currentTime = 0;
  1215 + scheduledLookup = [];
  1216 + scheduledFunctions = {};
  1217 + delayedFnCount = 0;
  1218 + };
  1219 +
  1220 + return self;
  1221 +
  1222 + function indexOfFirstToPass(array, testFn) {
  1223 + var index = -1;
  1224 +
  1225 + for (var i = 0; i < array.length; ++i) {
  1226 + if (testFn(array[i])) {
  1227 + index = i;
  1228 + break;
  1229 + }
  1230 + }
  1231 +
  1232 + return index;
  1233 + }
  1234 +
  1235 + function deleteFromLookup(key) {
  1236 + var value = Number(key);
  1237 + var i = indexOfFirstToPass(scheduledLookup, function (millis) {
  1238 + return millis === value;
  1239 + });
  1240 +
  1241 + if (i > -1) {
  1242 + scheduledLookup.splice(i, 1);
  1243 + }
  1244 + }
  1245 +
  1246 + function reschedule(scheduledFn) {
  1247 + self.scheduleFunction(scheduledFn.funcToCall,
  1248 + scheduledFn.millis,
  1249 + scheduledFn.params,
  1250 + true,
  1251 + scheduledFn.timeoutKey,
  1252 + scheduledFn.runAtMillis + scheduledFn.millis);
  1253 + }
  1254 +
  1255 + function forEachFunction(funcsToRun, callback) {
  1256 + for (var i = 0; i < funcsToRun.length; ++i) {
  1257 + callback(funcsToRun[i]);
  1258 + }
  1259 + }
  1260 +
  1261 + function runScheduledFunctions(endTime) {
  1262 + if (scheduledLookup.length === 0 || scheduledLookup[0] > endTime) {
  1263 + return;
  1264 + }
  1265 +
  1266 + do {
  1267 + currentTime = scheduledLookup.shift();
  1268 +
  1269 + var funcsToRun = scheduledFunctions[currentTime];
  1270 + delete scheduledFunctions[currentTime];
  1271 +
  1272 + forEachFunction(funcsToRun, function(funcToRun) {
  1273 + if (funcToRun.recurring) {
  1274 + reschedule(funcToRun);
  1275 + }
  1276 + });
  1277 +
  1278 + forEachFunction(funcsToRun, function(funcToRun) {
  1279 + funcToRun.funcToCall.apply(null, funcToRun.params || []);
  1280 + });
  1281 + } while (scheduledLookup.length > 0 &&
  1282 + // checking first if we're out of time prevents setTimeout(0)
  1283 + // scheduled in a funcToRun from forcing an extra iteration
  1284 + currentTime !== endTime &&
  1285 + scheduledLookup[0] <= endTime);
  1286 + }
  1287 + }
  1288 +
  1289 + return DelayedFunctionScheduler;
  1290 +};
  1291 +
  1292 +getJasmineRequireObj().ExceptionFormatter = function() {
  1293 + function ExceptionFormatter() {
  1294 + this.message = function(error) {
  1295 + var message = '';
  1296 +
  1297 + if (error.name && error.message) {
  1298 + message += error.name + ': ' + error.message;
  1299 + } else {
  1300 + message += error.toString() + ' thrown';
  1301 + }
  1302 +
  1303 + if (error.fileName || error.sourceURL) {
  1304 + message += ' in ' + (error.fileName || error.sourceURL);
  1305 + }
  1306 +
  1307 + if (error.line || error.lineNumber) {
  1308 + message += ' (line ' + (error.line || error.lineNumber) + ')';
  1309 + }
  1310 +
  1311 + return message;
  1312 + };
  1313 +
  1314 + this.stack = function(error) {
  1315 + return error ? error.stack : null;
  1316 + };
  1317 + }
  1318 +
  1319 + return ExceptionFormatter;
  1320 +};
  1321 +
  1322 +getJasmineRequireObj().Expectation = function() {
  1323 +
  1324 + function Expectation(options) {
  1325 + this.util = options.util || { buildFailureMessage: function() {} };
  1326 + this.customEqualityTesters = options.customEqualityTesters || [];
  1327 + this.actual = options.actual;
  1328 + this.addExpectationResult = options.addExpectationResult || function(){};
  1329 + this.isNot = options.isNot;
  1330 +
  1331 + var customMatchers = options.customMatchers || {};
  1332 + for (var matcherName in customMatchers) {
  1333 + this[matcherName] = Expectation.prototype.wrapCompare(matcherName, customMatchers[matcherName]);
  1334 + }
  1335 + }
  1336 +
  1337 + Expectation.prototype.wrapCompare = function(name, matcherFactory) {
  1338 + return function() {
  1339 + var args = Array.prototype.slice.call(arguments, 0),
  1340 + expected = args.slice(0),
  1341 + message = '';
  1342 +
  1343 + args.unshift(this.actual);
  1344 +
  1345 + var matcher = matcherFactory(this.util, this.customEqualityTesters),
  1346 + matcherCompare = matcher.compare;
  1347 +
  1348 + function defaultNegativeCompare() {
  1349 + var result = matcher.compare.apply(null, args);
  1350 + result.pass = !result.pass;
  1351 + return result;
  1352 + }
  1353 +
  1354 + if (this.isNot) {
  1355 + matcherCompare = matcher.negativeCompare || defaultNegativeCompare;
  1356 + }
  1357 +
  1358 + var result = matcherCompare.apply(null, args);
  1359 +
  1360 + if (!result.pass) {
  1361 + if (!result.message) {
  1362 + args.unshift(this.isNot);
  1363 + args.unshift(name);
  1364 + message = this.util.buildFailureMessage.apply(null, args);
  1365 + } else {
  1366 + if (Object.prototype.toString.apply(result.message) === '[object Function]') {
  1367 + message = result.message();
  1368 + } else {
  1369 + message = result.message;
  1370 + }
  1371 + }
  1372 + }
  1373 +
  1374 + if (expected.length == 1) {
  1375 + expected = expected[0];
  1376 + }
  1377 +
  1378 + // TODO: how many of these params are needed?
  1379 + this.addExpectationResult(
  1380 + result.pass,
  1381 + {
  1382 + matcherName: name,
  1383 + passed: result.pass,
  1384 + message: message,
  1385 + actual: this.actual,
  1386 + expected: expected // TODO: this may need to be arrayified/sliced
  1387 + }
  1388 + );
  1389 + };
  1390 + };
  1391 +
  1392 + Expectation.addCoreMatchers = function(matchers) {
  1393 + var prototype = Expectation.prototype;
  1394 + for (var matcherName in matchers) {
  1395 + var matcher = matchers[matcherName];
  1396 + prototype[matcherName] = prototype.wrapCompare(matcherName, matcher);
  1397 + }
  1398 + };
  1399 +
  1400 + Expectation.Factory = function(options) {
  1401 + options = options || {};
  1402 +
  1403 + var expect = new Expectation(options);
  1404 +
  1405 + // TODO: this would be nice as its own Object - NegativeExpectation
  1406 + // TODO: copy instead of mutate options
  1407 + options.isNot = true;
  1408 + expect.not = new Expectation(options);
  1409 +
  1410 + return expect;
  1411 + };
  1412 +
  1413 + return Expectation;
  1414 +};
  1415 +
  1416 +//TODO: expectation result may make more sense as a presentation of an expectation.
  1417 +getJasmineRequireObj().buildExpectationResult = function() {
  1418 + function buildExpectationResult(options) {
  1419 + var messageFormatter = options.messageFormatter || function() {},
  1420 + stackFormatter = options.stackFormatter || function() {};
  1421 +
  1422 + var result = {
  1423 + matcherName: options.matcherName,
  1424 + message: message(),
  1425 + stack: stack(),
  1426 + passed: options.passed
  1427 + };
  1428 +
  1429 + if(!result.passed) {
  1430 + result.expected = options.expected;
  1431 + result.actual = options.actual;
  1432 + }
  1433 +
  1434 + return result;
  1435 +
  1436 + function message() {
  1437 + if (options.passed) {
  1438 + return 'Passed.';
  1439 + } else if (options.message) {
  1440 + return options.message;
  1441 + } else if (options.error) {
  1442 + return messageFormatter(options.error);
  1443 + }
  1444 + return '';
  1445 + }
  1446 +
  1447 + function stack() {
  1448 + if (options.passed) {
  1449 + return '';
  1450 + }
  1451 +
  1452 + var error = options.error;
  1453 + if (!error) {
  1454 + try {
  1455 + throw new Error(message());
  1456 + } catch (e) {
  1457 + error = e;
  1458 + }
  1459 + }
  1460 + return stackFormatter(error);
  1461 + }
  1462 + }
  1463 +
  1464 + return buildExpectationResult;
  1465 +};
  1466 +
  1467 +getJasmineRequireObj().MockDate = function() {
  1468 + function MockDate(global) {
  1469 + var self = this;
  1470 + var currentTime = 0;
  1471 +
  1472 + if (!global || !global.Date) {
  1473 + self.install = function() {};
  1474 + self.tick = function() {};
  1475 + self.uninstall = function() {};
  1476 + return self;
  1477 + }
  1478 +
  1479 + var GlobalDate = global.Date;
  1480 +
  1481 + self.install = function(mockDate) {
  1482 + if (mockDate instanceof GlobalDate) {
  1483 + currentTime = mockDate.getTime();
  1484 + } else {
  1485 + currentTime = new GlobalDate().getTime();
  1486 + }
  1487 +
  1488 + global.Date = FakeDate;
  1489 + };
  1490 +
  1491 + self.tick = function(millis) {
  1492 + millis = millis || 0;
  1493 + currentTime = currentTime + millis;
  1494 + };
  1495 +
  1496 + self.uninstall = function() {
  1497 + currentTime = 0;
  1498 + global.Date = GlobalDate;
  1499 + };
  1500 +
  1501 + createDateProperties();
  1502 +
  1503 + return self;
  1504 +
  1505 + function FakeDate() {
  1506 + switch(arguments.length) {
  1507 + case 0:
  1508 + return new GlobalDate(currentTime);
  1509 + case 1:
  1510 + return new GlobalDate(arguments[0]);
  1511 + case 2:
  1512 + return new GlobalDate(arguments[0], arguments[1]);
  1513 + case 3:
  1514 + return new GlobalDate(arguments[0], arguments[1], arguments[2]);
  1515 + case 4:
  1516 + return new GlobalDate(arguments[0], arguments[1], arguments[2], arguments[3]);
  1517 + case 5:
  1518 + return new GlobalDate(arguments[0], arguments[1], arguments[2], arguments[3],
  1519 + arguments[4]);
  1520 + case 6:
  1521 + return new GlobalDate(arguments[0], arguments[1], arguments[2], arguments[3],
  1522 + arguments[4], arguments[5]);
  1523 + default:
  1524 + return new GlobalDate(arguments[0], arguments[1], arguments[2], arguments[3],
  1525 + arguments[4], arguments[5], arguments[6]);
  1526 + }
  1527 + }
  1528 +
  1529 + function createDateProperties() {
  1530 + FakeDate.prototype = GlobalDate.prototype;
  1531 +
  1532 + FakeDate.now = function() {
  1533 + if (GlobalDate.now) {
  1534 + return currentTime;
  1535 + } else {
  1536 + throw new Error('Browser does not support Date.now()');
  1537 + }
  1538 + };
  1539 +
  1540 + FakeDate.toSource = GlobalDate.toSource;
  1541 + FakeDate.toString = GlobalDate.toString;
  1542 + FakeDate.parse = GlobalDate.parse;
  1543 + FakeDate.UTC = GlobalDate.UTC;
  1544 + }
  1545 + }
  1546 +
  1547 + return MockDate;
  1548 +};
  1549 +
  1550 +getJasmineRequireObj().pp = function(j$) {
  1551 +
  1552 + function PrettyPrinter() {
  1553 + this.ppNestLevel_ = 0;
  1554 + this.seen = [];
  1555 + }
  1556 +
  1557 + PrettyPrinter.prototype.format = function(value) {
  1558 + this.ppNestLevel_++;
  1559 + try {
  1560 + if (j$.util.isUndefined(value)) {
  1561 + this.emitScalar('undefined');
  1562 + } else if (value === null) {
  1563 + this.emitScalar('null');
  1564 + } else if (value === 0 && 1/value === -Infinity) {
  1565 + this.emitScalar('-0');
  1566 + } else if (value === j$.getGlobal()) {
  1567 + this.emitScalar('<global>');
  1568 + } else if (value.jasmineToString) {
  1569 + this.emitScalar(value.jasmineToString());
  1570 + } else if (typeof value === 'string') {
  1571 + this.emitString(value);
  1572 + } else if (j$.isSpy(value)) {
  1573 + this.emitScalar('spy on ' + value.and.identity());
  1574 + } else if (value instanceof RegExp) {
  1575 + this.emitScalar(value.toString());
  1576 + } else if (typeof value === 'function') {
  1577 + this.emitScalar('Function');
  1578 + } else if (typeof value.nodeType === 'number') {
  1579 + this.emitScalar('HTMLNode');
  1580 + } else if (value instanceof Date) {
  1581 + this.emitScalar('Date(' + value + ')');
  1582 + } else if (j$.util.arrayContains(this.seen, value)) {
  1583 + this.emitScalar('<circular reference: ' + (j$.isArray_(value) ? 'Array' : 'Object') + '>');
  1584 + } else if (j$.isArray_(value) || j$.isA_('Object', value)) {
  1585 + this.seen.push(value);
  1586 + if (j$.isArray_(value)) {
  1587 + this.emitArray(value);
  1588 + } else {
  1589 + this.emitObject(value);
  1590 + }
  1591 + this.seen.pop();
  1592 + } else {
  1593 + this.emitScalar(value.toString());
  1594 + }
  1595 + } finally {
  1596 + this.ppNestLevel_--;
  1597 + }
  1598 + };
  1599 +
  1600 + PrettyPrinter.prototype.iterateObject = function(obj, fn) {
  1601 + for (var property in obj) {
  1602 + if (!Object.prototype.hasOwnProperty.call(obj, property)) { continue; }
  1603 + fn(property, obj.__lookupGetter__ ? (!j$.util.isUndefined(obj.__lookupGetter__(property)) &&
  1604 + obj.__lookupGetter__(property) !== null) : false);
  1605 + }
  1606 + };
  1607 +
  1608 + PrettyPrinter.prototype.emitArray = j$.unimplementedMethod_;
  1609 + PrettyPrinter.prototype.emitObject = j$.unimplementedMethod_;
  1610 + PrettyPrinter.prototype.emitScalar = j$.unimplementedMethod_;
  1611 + PrettyPrinter.prototype.emitString = j$.unimplementedMethod_;
  1612 +
  1613 + function StringPrettyPrinter() {
  1614 + PrettyPrinter.call(this);
  1615 +
  1616 + this.string = '';
  1617 + }
  1618 +
  1619 + j$.util.inherit(StringPrettyPrinter, PrettyPrinter);
  1620 +
  1621 + StringPrettyPrinter.prototype.emitScalar = function(value) {
  1622 + this.append(value);
  1623 + };
  1624 +
  1625 + StringPrettyPrinter.prototype.emitString = function(value) {
  1626 + this.append('\'' + value + '\'');
  1627 + };
  1628 +
  1629 + StringPrettyPrinter.prototype.emitArray = function(array) {
  1630 + if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) {
  1631 + this.append('Array');
  1632 + return;
  1633 + }
  1634 + var length = Math.min(array.length, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH);
  1635 + this.append('[ ');
  1636 + for (var i = 0; i < length; i++) {
  1637 + if (i > 0) {
  1638 + this.append(', ');
  1639 + }
  1640 + this.format(array[i]);
  1641 + }
  1642 + if(array.length > length){
  1643 + this.append(', ...');
  1644 + }
  1645 + this.append(' ]');
  1646 + };
  1647 +
  1648 + StringPrettyPrinter.prototype.emitObject = function(obj) {
  1649 + var constructorName = obj.constructor ? j$.fnNameFor(obj.constructor) : 'null';
  1650 + this.append(constructorName);
  1651 +
  1652 + if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) {
  1653 + return;
  1654 + }
  1655 +
  1656 + var self = this;
  1657 + this.append('({ ');
  1658 + var first = true;
  1659 +
  1660 + this.iterateObject(obj, function(property, isGetter) {
  1661 + if (first) {
  1662 + first = false;
  1663 + } else {
  1664 + self.append(', ');
  1665 + }
  1666 +
  1667 + self.append(property);
  1668 + self.append(': ');
  1669 + if (isGetter) {
  1670 + self.append('<getter>');
  1671 + } else {
  1672 + self.format(obj[property]);
  1673 + }
  1674 + });
  1675 +
  1676 + this.append(' })');
  1677 + };
  1678 +
  1679 + StringPrettyPrinter.prototype.append = function(value) {
  1680 + this.string += value;
  1681 + };
  1682 +
  1683 + return function(value) {
  1684 + var stringPrettyPrinter = new StringPrettyPrinter();
  1685 + stringPrettyPrinter.format(value);
  1686 + return stringPrettyPrinter.string;
  1687 + };
  1688 +};
  1689 +
  1690 +getJasmineRequireObj().QueueRunner = function(j$) {
  1691 +
  1692 + function once(fn) {
  1693 + var called = false;
  1694 + return function() {
  1695 + if (!called) {
  1696 + called = true;
  1697 + fn();
  1698 + }
  1699 + };
  1700 + }
  1701 +
  1702 + function QueueRunner(attrs) {
  1703 + this.queueableFns = attrs.queueableFns || [];
  1704 + this.onComplete = attrs.onComplete || function() {};
  1705 + this.clearStack = attrs.clearStack || function(fn) {fn();};
  1706 + this.onException = attrs.onException || function() {};
  1707 + this.catchException = attrs.catchException || function() { return true; };
  1708 + this.userContext = attrs.userContext || {};
  1709 + this.timer = attrs.timeout || {setTimeout: setTimeout, clearTimeout: clearTimeout};
  1710 + this.fail = attrs.fail || function() {};
  1711 + }
  1712 +
  1713 + QueueRunner.prototype.execute = function() {
  1714 + this.run(this.queueableFns, 0);
  1715 + };
  1716 +
  1717 + QueueRunner.prototype.run = function(queueableFns, recursiveIndex) {
  1718 + var length = queueableFns.length,
  1719 + self = this,
  1720 + iterativeIndex;
  1721 +
  1722 +
  1723 + for(iterativeIndex = recursiveIndex; iterativeIndex < length; iterativeIndex++) {
  1724 + var queueableFn = queueableFns[iterativeIndex];
  1725 + if (queueableFn.fn.length > 0) {
  1726 + attemptAsync(queueableFn);
  1727 + return;
  1728 + } else {
  1729 + attemptSync(queueableFn);
  1730 + }
  1731 + }
  1732 +
  1733 + var runnerDone = iterativeIndex >= length;
  1734 +
  1735 + if (runnerDone) {
  1736 + this.clearStack(this.onComplete);
  1737 + }
  1738 +
  1739 + function attemptSync(queueableFn) {
  1740 + try {
  1741 + queueableFn.fn.call(self.userContext);
  1742 + } catch (e) {
  1743 + handleException(e, queueableFn);
  1744 + }
  1745 + }
  1746 +
  1747 + function attemptAsync(queueableFn) {
  1748 + var clearTimeout = function () {
  1749 + Function.prototype.apply.apply(self.timer.clearTimeout, [j$.getGlobal(), [timeoutId]]);
  1750 + },
  1751 + next = once(function () {
  1752 + clearTimeout(timeoutId);
  1753 + self.run(queueableFns, iterativeIndex + 1);
  1754 + }),
  1755 + timeoutId;
  1756 +
  1757 + next.fail = function() {
  1758 + self.fail.apply(null, arguments);
  1759 + next();
  1760 + };
  1761 +
  1762 + if (queueableFn.timeout) {
  1763 + timeoutId = Function.prototype.apply.apply(self.timer.setTimeout, [j$.getGlobal(), [function() {
  1764 + var error = new Error('Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.');
  1765 + onException(error, queueableFn);
  1766 + next();
  1767 + }, queueableFn.timeout()]]);
  1768 + }
  1769 +
  1770 + try {
  1771 + queueableFn.fn.call(self.userContext, next);
  1772 + } catch (e) {
  1773 + handleException(e, queueableFn);
  1774 + next();
  1775 + }
  1776 + }
  1777 +
  1778 + function onException(e, queueableFn) {
  1779 + self.onException(e);
  1780 + }
  1781 +
  1782 + function handleException(e, queueableFn) {
  1783 + onException(e, queueableFn);
  1784 + if (!self.catchException(e)) {
  1785 + //TODO: set a var when we catch an exception and
  1786 + //use a finally block to close the loop in a nice way..
  1787 + throw e;
  1788 + }
  1789 + }
  1790 + };
  1791 +
  1792 + return QueueRunner;
  1793 +};
  1794 +
  1795 +getJasmineRequireObj().ReportDispatcher = function() {
  1796 + function ReportDispatcher(methods) {
  1797 +
  1798 + var dispatchedMethods = methods || [];
  1799 +
  1800 + for (var i = 0; i < dispatchedMethods.length; i++) {
  1801 + var method = dispatchedMethods[i];
  1802 + this[method] = (function(m) {
  1803 + return function() {
  1804 + dispatch(m, arguments);
  1805 + };
  1806 + }(method));
  1807 + }
  1808 +
  1809 + var reporters = [];
  1810 +
  1811 + this.addReporter = function(reporter) {
  1812 + reporters.push(reporter);
  1813 + };
  1814 +
  1815 + return this;
  1816 +
  1817 + function dispatch(method, args) {
  1818 + for (var i = 0; i < reporters.length; i++) {
  1819 + var reporter = reporters[i];
  1820 + if (reporter[method]) {
  1821 + reporter[method].apply(reporter, args);
  1822 + }
  1823 + }
  1824 + }
  1825 + }
  1826 +
  1827 + return ReportDispatcher;
  1828 +};
  1829 +
  1830 +
  1831 +getJasmineRequireObj().SpyRegistry = function(j$) {
  1832 +
  1833 + function SpyRegistry(options) {
  1834 + options = options || {};
  1835 + var currentSpies = options.currentSpies || function() { return []; };
  1836 +
  1837 + this.spyOn = function(obj, methodName) {
  1838 + if (j$.util.isUndefined(obj)) {
  1839 + throw new Error('spyOn could not find an object to spy upon for ' + methodName + '()');
  1840 + }
  1841 +
  1842 + if (j$.util.isUndefined(methodName)) {
  1843 + throw new Error('No method name supplied');
  1844 + }
  1845 +
  1846 + if (j$.util.isUndefined(obj[methodName])) {
  1847 + throw new Error(methodName + '() method does not exist');
  1848 + }
  1849 +
  1850 + if (obj[methodName] && j$.isSpy(obj[methodName])) {
  1851 + //TODO?: should this return the current spy? Downside: may cause user confusion about spy state
  1852 + throw new Error(methodName + ' has already been spied upon');
  1853 + }
  1854 +
  1855 + var spy = j$.createSpy(methodName, obj[methodName]);
  1856 +
  1857 + currentSpies().push({
  1858 + spy: spy,
  1859 + baseObj: obj,
  1860 + methodName: methodName,
  1861 + originalValue: obj[methodName]
  1862 + });
  1863 +
  1864 + obj[methodName] = spy;
  1865 +
  1866 + return spy;
  1867 + };
  1868 +
  1869 + this.clearSpies = function() {
  1870 + var spies = currentSpies();
  1871 + for (var i = 0; i < spies.length; i++) {
  1872 + var spyEntry = spies[i];
  1873 + spyEntry.baseObj[spyEntry.methodName] = spyEntry.originalValue;
  1874 + }
  1875 + };
  1876 + }
  1877 +
  1878 + return SpyRegistry;
  1879 +};
  1880 +
  1881 +getJasmineRequireObj().SpyStrategy = function() {
  1882 +
  1883 + function SpyStrategy(options) {
  1884 + options = options || {};
  1885 +
  1886 + var identity = options.name || 'unknown',
  1887 + originalFn = options.fn || function() {},
  1888 + getSpy = options.getSpy || function() {},
  1889 + plan = function() {};
  1890 +
  1891 + this.identity = function() {
  1892 + return identity;
  1893 + };
  1894 +
  1895 + this.exec = function() {
  1896 + return plan.apply(this, arguments);
  1897 + };
  1898 +
  1899 + this.callThrough = function() {
  1900 + plan = originalFn;
  1901 + return getSpy();
  1902 + };
  1903 +
  1904 + this.returnValue = function(value) {
  1905 + plan = function() {
  1906 + return value;
  1907 + };
  1908 + return getSpy();
  1909 + };
  1910 +
  1911 + this.returnValues = function() {
  1912 + var values = Array.prototype.slice.call(arguments);
  1913 + plan = function () {
  1914 + return values.shift();
  1915 + };
  1916 + return getSpy();
  1917 + };
  1918 +
  1919 + this.throwError = function(something) {
  1920 + var error = (something instanceof Error) ? something : new Error(something);
  1921 + plan = function() {
  1922 + throw error;
  1923 + };
  1924 + return getSpy();
  1925 + };
  1926 +
  1927 + this.callFake = function(fn) {
  1928 + plan = fn;
  1929 + return getSpy();
  1930 + };
  1931 +
  1932 + this.stub = function(fn) {
  1933 + plan = function() {};
  1934 + return getSpy();
  1935 + };
  1936 + }
  1937 +
  1938 + return SpyStrategy;
  1939 +};
  1940 +
  1941 +getJasmineRequireObj().Suite = function() {
  1942 + function Suite(attrs) {
  1943 + this.env = attrs.env;
  1944 + this.id = attrs.id;
  1945 + this.parentSuite = attrs.parentSuite;
  1946 + this.description = attrs.description;
  1947 + this.onStart = attrs.onStart || function() {};
  1948 + this.resultCallback = attrs.resultCallback || function() {};
  1949 + this.clearStack = attrs.clearStack || function(fn) {fn();};
  1950 + this.expectationFactory = attrs.expectationFactory;
  1951 + this.expectationResultFactory = attrs.expectationResultFactory;
  1952 + this.runnablesExplictlySetGetter = attrs.runnablesExplictlySetGetter || function() {};
  1953 +
  1954 + this.beforeFns = [];
  1955 + this.afterFns = [];
  1956 + this.beforeAllFns = [];
  1957 + this.afterAllFns = [];
  1958 + this.queueRunner = attrs.queueRunner || function() {};
  1959 + this.disabled = false;
  1960 +
  1961 + this.children = [];
  1962 +
  1963 + this.result = {
  1964 + id: this.id,
  1965 + description: this.description,
  1966 + fullName: this.getFullName(),
  1967 + failedExpectations: []
  1968 + };
  1969 + }
  1970 +
  1971 + Suite.prototype.expect = function(actual) {
  1972 + return this.expectationFactory(actual, this);
  1973 + };
  1974 +
  1975 + Suite.prototype.getFullName = function() {
  1976 + var fullName = this.description;
  1977 + for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) {
  1978 + if (parentSuite.parentSuite) {
  1979 + fullName = parentSuite.description + ' ' + fullName;
  1980 + }
  1981 + }
  1982 + return fullName;
  1983 + };
  1984 +
  1985 + Suite.prototype.disable = function() {
  1986 + this.disabled = true;
  1987 + };
  1988 +
  1989 + Suite.prototype.beforeEach = function(fn) {
  1990 + this.beforeFns.unshift(fn);
  1991 + };
  1992 +
  1993 + Suite.prototype.beforeAll = function(fn) {
  1994 + this.beforeAllFns.push(fn);
  1995 + };
  1996 +
  1997 + Suite.prototype.afterEach = function(fn) {
  1998 + this.afterFns.unshift(fn);
  1999 + };
  2000 +
  2001 + Suite.prototype.afterAll = function(fn) {
  2002 + this.afterAllFns.push(fn);
  2003 + };
  2004 +
  2005 + Suite.prototype.addChild = function(child) {
  2006 + this.children.push(child);
  2007 + };
  2008 +
  2009 + Suite.prototype.status = function() {
  2010 + if (this.disabled) {
  2011 + return 'disabled';
  2012 + }
  2013 +
  2014 + if (this.result.failedExpectations.length > 0) {
  2015 + return 'failed';
  2016 + } else {
  2017 + return 'finished';
  2018 + }
  2019 + };
  2020 +
  2021 + Suite.prototype.execute = function(onComplete) {
  2022 + var self = this;
  2023 +
  2024 + this.onStart(this);
  2025 +
  2026 + if (this.disabled) {
  2027 + complete();
  2028 + return;
  2029 + }
  2030 +
  2031 + var allFns = [];
  2032 +
  2033 + for (var i = 0; i < this.children.length; i++) {
  2034 + allFns.push(wrapChildAsAsync(this.children[i]));
  2035 + }
  2036 +
  2037 + if (this.isExecutable()) {
  2038 + allFns = this.beforeAllFns.concat(allFns);
  2039 + allFns = allFns.concat(this.afterAllFns);
  2040 + }
  2041 +
  2042 + this.queueRunner({
  2043 + queueableFns: allFns,
  2044 + onComplete: complete,
  2045 + userContext: this.sharedUserContext(),
  2046 + onException: function() { self.onException.apply(self, arguments); }
  2047 + });
  2048 +
  2049 + function complete() {
  2050 + self.result.status = self.status();
  2051 + self.resultCallback(self.result);
  2052 +
  2053 + if (onComplete) {
  2054 + onComplete();
  2055 + }
  2056 + }
  2057 +
  2058 + function wrapChildAsAsync(child) {
  2059 + return { fn: function(done) { child.execute(done); } };
  2060 + }
  2061 + };
  2062 +
  2063 + Suite.prototype.isExecutable = function() {
  2064 + var runnablesExplicitlySet = this.runnablesExplictlySetGetter();
  2065 + return !runnablesExplicitlySet && hasExecutableChild(this.children);
  2066 + };
  2067 +
  2068 + Suite.prototype.sharedUserContext = function() {
  2069 + if (!this.sharedContext) {
  2070 + this.sharedContext = this.parentSuite ? clone(this.parentSuite.sharedUserContext()) : {};
  2071 + }
  2072 +
  2073 + return this.sharedContext;
  2074 + };
  2075 +
  2076 + Suite.prototype.clonedSharedUserContext = function() {
  2077 + return clone(this.sharedUserContext());
  2078 + };
  2079 +
  2080 + Suite.prototype.onException = function() {
  2081 + if(isAfterAll(this.children)) {
  2082 + var data = {
  2083 + matcherName: '',
  2084 + passed: false,
  2085 + expected: '',
  2086 + actual: '',
  2087 + error: arguments[0]
  2088 + };
  2089 + this.result.failedExpectations.push(this.expectationResultFactory(data));
  2090 + } else {
  2091 + for (var i = 0; i < this.children.length; i++) {
  2092 + var child = this.children[i];
  2093 + child.onException.apply(child, arguments);
  2094 + }
  2095 + }
  2096 + };
  2097 +
  2098 + Suite.prototype.addExpectationResult = function () {
  2099 + if(isAfterAll(this.children) && isFailure(arguments)){
  2100 + var data = arguments[1];
  2101 + this.result.failedExpectations.push(this.expectationResultFactory(data));
  2102 + } else {
  2103 + for (var i = 0; i < this.children.length; i++) {
  2104 + var child = this.children[i];
  2105 + child.addExpectationResult.apply(child, arguments);
  2106 + }
  2107 + }
  2108 + };
  2109 +
  2110 + function isAfterAll(children) {
  2111 + return children && children[0].result.status;
  2112 + }
  2113 +
  2114 + function isFailure(args) {
  2115 + return !args[0];
  2116 + }
  2117 +
  2118 + function hasExecutableChild(children) {
  2119 + var foundActive = false;
  2120 + for (var i = 0; i < children.length; i++) {
  2121 + if (children[i].isExecutable()) {
  2122 + foundActive = true;
  2123 + break;
  2124 + }
  2125 + }
  2126 + return foundActive;
  2127 + }
  2128 +
  2129 + function clone(obj) {
  2130 + var clonedObj = {};
  2131 + for (var prop in obj) {
  2132 + if (obj.hasOwnProperty(prop)) {
  2133 + clonedObj[prop] = obj[prop];
  2134 + }
  2135 + }
  2136 +
  2137 + return clonedObj;
  2138 + }
  2139 +
  2140 + return Suite;
  2141 +};
  2142 +
  2143 +if (typeof window == void 0 && typeof exports == 'object') {
  2144 + exports.Suite = jasmineRequire.Suite;
  2145 +}
  2146 +
  2147 +getJasmineRequireObj().Timer = function() {
  2148 + var defaultNow = (function(Date) {
  2149 + return function() { return new Date().getTime(); };
  2150 + })(Date);
  2151 +
  2152 + function Timer(options) {
  2153 + options = options || {};
  2154 +
  2155 + var now = options.now || defaultNow,
  2156 + startTime;
  2157 +
  2158 + this.start = function() {
  2159 + startTime = now();
  2160 + };
  2161 +
  2162 + this.elapsed = function() {
  2163 + return now() - startTime;
  2164 + };
  2165 + }
  2166 +
  2167 + return Timer;
  2168 +};
  2169 +
  2170 +getJasmineRequireObj().Any = function() {
  2171 +
  2172 + function Any(expectedObject) {
  2173 + this.expectedObject = expectedObject;
  2174 + }
  2175 +
  2176 + Any.prototype.asymmetricMatch = function(other) {
  2177 + if (this.expectedObject == String) {
  2178 + return typeof other == 'string' || other instanceof String;
  2179 + }
  2180 +
  2181 + if (this.expectedObject == Number) {
  2182 + return typeof other == 'number' || other instanceof Number;
  2183 + }
  2184 +
  2185 + if (this.expectedObject == Function) {
  2186 + return typeof other == 'function' || other instanceof Function;
  2187 + }
  2188 +
  2189 + if (this.expectedObject == Object) {
  2190 + return typeof other == 'object';
  2191 + }
  2192 +
  2193 + if (this.expectedObject == Boolean) {
  2194 + return typeof other == 'boolean';
  2195 + }
  2196 +
  2197 + return other instanceof this.expectedObject;
  2198 + };
  2199 +
  2200 + Any.prototype.jasmineToString = function() {
  2201 + return '<jasmine.any(' + this.expectedObject + ')>';
  2202 + };
  2203 +
  2204 + return Any;
  2205 +};
  2206 +
  2207 +getJasmineRequireObj().Anything = function(j$) {
  2208 +
  2209 + function Anything() {}
  2210 +
  2211 + Anything.prototype.asymmetricMatch = function(other) {
  2212 + return !j$.util.isUndefined(other) && other !== null;
  2213 + };
  2214 +
  2215 + Anything.prototype.jasmineToString = function() {
  2216 + return '<jasmine.anything>';
  2217 + };
  2218 +
  2219 + return Anything;
  2220 +};
  2221 +
  2222 +getJasmineRequireObj().ArrayContaining = function(j$) {
  2223 + function ArrayContaining(sample) {
  2224 + this.sample = sample;
  2225 + }
  2226 +
  2227 + ArrayContaining.prototype.asymmetricMatch = function(other) {
  2228 + var className = Object.prototype.toString.call(this.sample);
  2229 + if (className !== '[object Array]') { throw new Error('You must provide an array to arrayContaining, not \'' + this.sample + '\'.'); }
  2230 +
  2231 + for (var i = 0; i < this.sample.length; i++) {
  2232 + var item = this.sample[i];
  2233 + if (!j$.matchersUtil.contains(other, item)) {
  2234 + return false;
  2235 + }
  2236 + }
  2237 +
  2238 + return true;
  2239 + };
  2240 +
  2241 + ArrayContaining.prototype.jasmineToString = function () {
  2242 + return '<jasmine.arrayContaining(' + jasmine.pp(this.sample) +')>';
  2243 + };
  2244 +
  2245 + return ArrayContaining;
  2246 +};
  2247 +
  2248 +getJasmineRequireObj().ObjectContaining = function(j$) {
  2249 +
  2250 + function ObjectContaining(sample) {
  2251 + this.sample = sample;
  2252 + }
  2253 +
  2254 + ObjectContaining.prototype.asymmetricMatch = function(other) {
  2255 + if (typeof(this.sample) !== 'object') { throw new Error('You must provide an object to objectContaining, not \''+this.sample+'\'.'); }
  2256 +
  2257 + for (var property in this.sample) {
  2258 + if (!Object.prototype.hasOwnProperty.call(other, property) ||
  2259 + !j$.matchersUtil.equals(this.sample[property], other[property])) {
  2260 + return false;
  2261 + }
  2262 + }
  2263 +
  2264 + return true;
  2265 + };
  2266 +
  2267 + ObjectContaining.prototype.jasmineToString = function() {
  2268 + return '<jasmine.objectContaining(' + j$.pp(this.sample) + ')>';
  2269 + };
  2270 +
  2271 + return ObjectContaining;
  2272 +};
  2273 +
  2274 +getJasmineRequireObj().StringMatching = function(j$) {
  2275 +
  2276 + function StringMatching(expected) {
  2277 + if (!j$.isString_(expected) && !j$.isA_('RegExp', expected)) {
  2278 + throw new Error('Expected is not a String or a RegExp');
  2279 + }
  2280 +
  2281 + this.regexp = new RegExp(expected);
  2282 + }
  2283 +
  2284 + StringMatching.prototype.asymmetricMatch = function(other) {
  2285 + return this.regexp.test(other);
  2286 + };
  2287 +
  2288 + StringMatching.prototype.jasmineToString = function() {
  2289 + return '<jasmine.stringMatching(' + this.regexp + ')>';
  2290 + };
  2291 +
  2292 + return StringMatching;
  2293 +};
  2294 +
  2295 +getJasmineRequireObj().matchersUtil = function(j$) {
  2296 + // TODO: what to do about jasmine.pp not being inject? move to JSON.stringify? gut PrettyPrinter?
  2297 +
  2298 + return {
  2299 + equals: function(a, b, customTesters) {
  2300 + customTesters = customTesters || [];
  2301 +
  2302 + return eq(a, b, [], [], customTesters);
  2303 + },
  2304 +
  2305 + contains: function(haystack, needle, customTesters) {
  2306 + customTesters = customTesters || [];
  2307 +
  2308 + if ((Object.prototype.toString.apply(haystack) === '[object Array]') ||
  2309 + (!!haystack && !haystack.indexOf))
  2310 + {
  2311 + for (var i = 0; i < haystack.length; i++) {
  2312 + if (eq(haystack[i], needle, [], [], customTesters)) {
  2313 + return true;
  2314 + }
  2315 + }
  2316 + return false;
  2317 + }
  2318 +
  2319 + return !!haystack && haystack.indexOf(needle) >= 0;
  2320 + },
  2321 +
  2322 + buildFailureMessage: function() {
  2323 + var args = Array.prototype.slice.call(arguments, 0),
  2324 + matcherName = args[0],
  2325 + isNot = args[1],
  2326 + actual = args[2],
  2327 + expected = args.slice(3),
  2328 + englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); });
  2329 +
  2330 + var message = 'Expected ' +
  2331 + j$.pp(actual) +
  2332 + (isNot ? ' not ' : ' ') +
  2333 + englishyPredicate;
  2334 +
  2335 + if (expected.length > 0) {
  2336 + for (var i = 0; i < expected.length; i++) {
  2337 + if (i > 0) {
  2338 + message += ',';
  2339 + }
  2340 + message += ' ' + j$.pp(expected[i]);
  2341 + }
  2342 + }
  2343 +
  2344 + return message + '.';
  2345 + }
  2346 + };
  2347 +
  2348 + function isAsymmetric(obj) {
  2349 + return obj && j$.isA_('Function', obj.asymmetricMatch);
  2350 + }
  2351 +
  2352 + function asymmetricMatch(a, b) {
  2353 + var asymmetricA = isAsymmetric(a),
  2354 + asymmetricB = isAsymmetric(b);
  2355 +
  2356 + if (asymmetricA && asymmetricB) {
  2357 + return undefined;
  2358 + }
  2359 +
  2360 + if (asymmetricA) {
  2361 + return a.asymmetricMatch(b);
  2362 + }
  2363 +
  2364 + if (asymmetricB) {
  2365 + return b.asymmetricMatch(a);
  2366 + }
  2367 + }
  2368 +
  2369 + // Equality function lovingly adapted from isEqual in
  2370 + // [Underscore](http://underscorejs.org)
  2371 + function eq(a, b, aStack, bStack, customTesters) {
  2372 + var result = true;
  2373 +
  2374 + var asymmetricResult = asymmetricMatch(a, b);
  2375 + if (!j$.util.isUndefined(asymmetricResult)) {
  2376 + return asymmetricResult;
  2377 + }
  2378 +
  2379 + for (var i = 0; i < customTesters.length; i++) {
  2380 + var customTesterResult = customTesters[i](a, b);
  2381 + if (!j$.util.isUndefined(customTesterResult)) {
  2382 + return customTesterResult;
  2383 + }
  2384 + }
  2385 +
  2386 + if (a instanceof Error && b instanceof Error) {
  2387 + return a.message == b.message;
  2388 + }
  2389 +
  2390 + // Identical objects are equal. `0 === -0`, but they aren't identical.
  2391 + // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
  2392 + if (a === b) { return a !== 0 || 1 / a == 1 / b; }
  2393 + // A strict comparison is necessary because `null == undefined`.
  2394 + if (a === null || b === null) { return a === b; }
  2395 + var className = Object.prototype.toString.call(a);
  2396 + if (className != Object.prototype.toString.call(b)) { return false; }
  2397 + switch (className) {
  2398 + // Strings, numbers, dates, and booleans are compared by value.
  2399 + case '[object String]':
  2400 + // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
  2401 + // equivalent to `new String("5")`.
  2402 + return a == String(b);
  2403 + case '[object Number]':
  2404 + // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
  2405 + // other numeric values.
  2406 + return a != +a ? b != +b : (a === 0 ? 1 / a == 1 / b : a == +b);
  2407 + case '[object Date]':
  2408 + case '[object Boolean]':
  2409 + // Coerce dates and booleans to numeric primitive values. Dates are compared by their
  2410 + // millisecond representations. Note that invalid dates with millisecond representations
  2411 + // of `NaN` are not equivalent.
  2412 + return +a == +b;
  2413 + // RegExps are compared by their source patterns and flags.
  2414 + case '[object RegExp]':
  2415 + return a.source == b.source &&
  2416 + a.global == b.global &&
  2417 + a.multiline == b.multiline &&
  2418 + a.ignoreCase == b.ignoreCase;
  2419 + }
  2420 + if (typeof a != 'object' || typeof b != 'object') { return false; }
  2421 +
  2422 + var aIsDomNode = j$.isDomNode(a);
  2423 + var bIsDomNode = j$.isDomNode(b);
  2424 + if (aIsDomNode && bIsDomNode) {
  2425 + // At first try to use DOM3 method isEqualNode
  2426 + if (a.isEqualNode) {
  2427 + return a.isEqualNode(b);
  2428 + }
  2429 + // IE8 doesn't support isEqualNode, try to use outerHTML && innerText
  2430 + var aIsElement = a instanceof Element;
  2431 + var bIsElement = b instanceof Element;
  2432 + if (aIsElement && bIsElement) {
  2433 + return a.outerHTML == b.outerHTML;
  2434 + }
  2435 + if (aIsElement || bIsElement) {
  2436 + return false;
  2437 + }
  2438 + return a.innerText == b.innerText && a.textContent == b.textContent;
  2439 + }
  2440 + if (aIsDomNode || bIsDomNode) {
  2441 + return false;
  2442 + }
  2443 +
  2444 + // Assume equality for cyclic structures. The algorithm for detecting cyclic
  2445 + // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
  2446 + var length = aStack.length;
  2447 + while (length--) {
  2448 + // Linear search. Performance is inversely proportional to the number of
  2449 + // unique nested structures.
  2450 + if (aStack[length] == a) { return bStack[length] == b; }
  2451 + }
  2452 + // Add the first object to the stack of traversed objects.
  2453 + aStack.push(a);
  2454 + bStack.push(b);
  2455 + var size = 0;
  2456 + // Recursively compare objects and arrays.
  2457 + // Compare array lengths to determine if a deep comparison is necessary.
  2458 + if (className == '[object Array]' && a.length !== b.length) {
  2459 + result = false;
  2460 + }
  2461 +
  2462 + if (result) {
  2463 + // Objects with different constructors are not equivalent, but `Object`s
  2464 + // from different frames are.
  2465 + var aCtor = a.constructor, bCtor = b.constructor;
  2466 + if (aCtor !== bCtor && !(isFunction(aCtor) && (aCtor instanceof aCtor) &&
  2467 + isFunction(bCtor) && (bCtor instanceof bCtor))) {
  2468 + return false;
  2469 + }
  2470 + // Deep compare objects.
  2471 + for (var key in a) {
  2472 + if (has(a, key)) {
  2473 + // Count the expected number of properties.
  2474 + size++;
  2475 + // Deep compare each member.
  2476 + if (!(result = has(b, key) && eq(a[key], b[key], aStack, bStack, customTesters))) { break; }
  2477 + }
  2478 + }
  2479 + // Ensure that both objects contain the same number of properties.
  2480 + if (result) {
  2481 + for (key in b) {
  2482 + if (has(b, key) && !(size--)) { break; }
  2483 + }
  2484 + result = !size;
  2485 + }
  2486 + }
  2487 + // Remove the first object from the stack of traversed objects.
  2488 + aStack.pop();
  2489 + bStack.pop();
  2490 +
  2491 + return result;
  2492 +
  2493 + function has(obj, key) {
  2494 + return Object.prototype.hasOwnProperty.call(obj, key);
  2495 + }
  2496 +
  2497 + function isFunction(obj) {
  2498 + return typeof obj === 'function';
  2499 + }
  2500 + }
  2501 +};
  2502 +
  2503 +getJasmineRequireObj().toBe = function() {
  2504 + function toBe() {
  2505 + return {
  2506 + compare: function(actual, expected) {
  2507 + return {
  2508 + pass: actual === expected
  2509 + };
  2510 + }
  2511 + };
  2512 + }
  2513 +
  2514 + return toBe;
  2515 +};
  2516 +
  2517 +getJasmineRequireObj().toBeCloseTo = function() {
  2518 +
  2519 + function toBeCloseTo() {
  2520 + return {
  2521 + compare: function(actual, expected, precision) {
  2522 + if (precision !== 0) {
  2523 + precision = precision || 2;
  2524 + }
  2525 +
  2526 + return {
  2527 + pass: Math.abs(expected - actual) < (Math.pow(10, -precision) / 2)
  2528 + };
  2529 + }
  2530 + };
  2531 + }
  2532 +
  2533 + return toBeCloseTo;
  2534 +};
  2535 +
  2536 +getJasmineRequireObj().toBeDefined = function() {
  2537 + function toBeDefined() {
  2538 + return {
  2539 + compare: function(actual) {
  2540 + return {
  2541 + pass: (void 0 !== actual)
  2542 + };
  2543 + }
  2544 + };
  2545 + }
  2546 +
  2547 + return toBeDefined;
  2548 +};
  2549 +
  2550 +getJasmineRequireObj().toBeFalsy = function() {
  2551 + function toBeFalsy() {
  2552 + return {
  2553 + compare: function(actual) {
  2554 + return {
  2555 + pass: !!!actual
  2556 + };
  2557 + }
  2558 + };
  2559 + }
  2560 +
  2561 + return toBeFalsy;
  2562 +};
  2563 +
  2564 +getJasmineRequireObj().toBeGreaterThan = function() {
  2565 +
  2566 + function toBeGreaterThan() {
  2567 + return {
  2568 + compare: function(actual, expected) {
  2569 + return {
  2570 + pass: actual > expected
  2571 + };
  2572 + }
  2573 + };
  2574 + }
  2575 +
  2576 + return toBeGreaterThan;
  2577 +};
  2578 +
  2579 +
  2580 +getJasmineRequireObj().toBeLessThan = function() {
  2581 + function toBeLessThan() {
  2582 + return {
  2583 +
  2584 + compare: function(actual, expected) {
  2585 + return {
  2586 + pass: actual < expected
  2587 + };
  2588 + }
  2589 + };
  2590 + }
  2591 +
  2592 + return toBeLessThan;
  2593 +};
  2594 +getJasmineRequireObj().toBeNaN = function(j$) {
  2595 +
  2596 + function toBeNaN() {
  2597 + return {
  2598 + compare: function(actual) {
  2599 + var result = {
  2600 + pass: (actual !== actual)
  2601 + };
  2602 +
  2603 + if (result.pass) {
  2604 + result.message = 'Expected actual not to be NaN.';
  2605 + } else {
  2606 + result.message = function() { return 'Expected ' + j$.pp(actual) + ' to be NaN.'; };
  2607 + }
  2608 +
  2609 + return result;
  2610 + }
  2611 + };
  2612 + }
  2613 +
  2614 + return toBeNaN;
  2615 +};
  2616 +
  2617 +getJasmineRequireObj().toBeNull = function() {
  2618 +
  2619 + function toBeNull() {
  2620 + return {
  2621 + compare: function(actual) {
  2622 + return {
  2623 + pass: actual === null
  2624 + };
  2625 + }
  2626 + };
  2627 + }
  2628 +
  2629 + return toBeNull;
  2630 +};
  2631 +
  2632 +getJasmineRequireObj().toBeTruthy = function() {
  2633 +
  2634 + function toBeTruthy() {
  2635 + return {
  2636 + compare: function(actual) {
  2637 + return {
  2638 + pass: !!actual
  2639 + };
  2640 + }
  2641 + };
  2642 + }
  2643 +
  2644 + return toBeTruthy;
  2645 +};
  2646 +
  2647 +getJasmineRequireObj().toBeUndefined = function() {
  2648 +
  2649 + function toBeUndefined() {
  2650 + return {
  2651 + compare: function(actual) {
  2652 + return {
  2653 + pass: void 0 === actual
  2654 + };
  2655 + }
  2656 + };
  2657 + }
  2658 +
  2659 + return toBeUndefined;
  2660 +};
  2661 +
  2662 +getJasmineRequireObj().toContain = function() {
  2663 + function toContain(util, customEqualityTesters) {
  2664 + customEqualityTesters = customEqualityTesters || [];
  2665 +
  2666 + return {
  2667 + compare: function(actual, expected) {
  2668 +
  2669 + return {
  2670 + pass: util.contains(actual, expected, customEqualityTesters)
  2671 + };
  2672 + }
  2673 + };
  2674 + }
  2675 +
  2676 + return toContain;
  2677 +};
  2678 +
  2679 +getJasmineRequireObj().toEqual = function() {
  2680 +
  2681 + function toEqual(util, customEqualityTesters) {
  2682 + customEqualityTesters = customEqualityTesters || [];
  2683 +
  2684 + return {
  2685 + compare: function(actual, expected) {
  2686 + var result = {
  2687 + pass: false
  2688 + };
  2689 +
  2690 + result.pass = util.equals(actual, expected, customEqualityTesters);
  2691 +
  2692 + return result;
  2693 + }
  2694 + };
  2695 + }
  2696 +
  2697 + return toEqual;
  2698 +};
  2699 +
  2700 +getJasmineRequireObj().toHaveBeenCalled = function(j$) {
  2701 +
  2702 + function toHaveBeenCalled() {
  2703 + return {
  2704 + compare: function(actual) {
  2705 + var result = {};
  2706 +
  2707 + if (!j$.isSpy(actual)) {
  2708 + throw new Error('Expected a spy, but got ' + j$.pp(actual) + '.');
  2709 + }
  2710 +
  2711 + if (arguments.length > 1) {
  2712 + throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith');
  2713 + }
  2714 +
  2715 + result.pass = actual.calls.any();
  2716 +
  2717 + result.message = result.pass ?
  2718 + 'Expected spy ' + actual.and.identity() + ' not to have been called.' :
  2719 + 'Expected spy ' + actual.and.identity() + ' to have been called.';
  2720 +
  2721 + return result;
  2722 + }
  2723 + };
  2724 + }
  2725 +
  2726 + return toHaveBeenCalled;
  2727 +};
  2728 +
  2729 +getJasmineRequireObj().toHaveBeenCalledWith = function(j$) {
  2730 +
  2731 + function toHaveBeenCalledWith(util, customEqualityTesters) {
  2732 + return {
  2733 + compare: function() {
  2734 + var args = Array.prototype.slice.call(arguments, 0),
  2735 + actual = args[0],
  2736 + expectedArgs = args.slice(1),
  2737 + result = { pass: false };
  2738 +
  2739 + if (!j$.isSpy(actual)) {
  2740 + throw new Error('Expected a spy, but got ' + j$.pp(actual) + '.');
  2741 + }
  2742 +
  2743 + if (!actual.calls.any()) {
  2744 + result.message = function() { return 'Expected spy ' + actual.and.identity() + ' to have been called with ' + j$.pp(expectedArgs) + ' but it was never called.'; };
  2745 + return result;
  2746 + }
  2747 +
  2748 + if (util.contains(actual.calls.allArgs(), expectedArgs, customEqualityTesters)) {
  2749 + result.pass = true;
  2750 + result.message = function() { return 'Expected spy ' + actual.and.identity() + ' not to have been called with ' + j$.pp(expectedArgs) + ' but it was.'; };
  2751 + } else {
  2752 + result.message = function() { return 'Expected spy ' + actual.and.identity() + ' to have been called with ' + j$.pp(expectedArgs) + ' but actual calls were ' + j$.pp(actual.calls.allArgs()).replace(/^\[ | \]$/g, '') + '.'; };
  2753 + }
  2754 +
  2755 + return result;
  2756 + }
  2757 + };
  2758 + }
  2759 +
  2760 + return toHaveBeenCalledWith;
  2761 +};
  2762 +
  2763 +getJasmineRequireObj().toMatch = function(j$) {
  2764 +
  2765 + function toMatch() {
  2766 + return {
  2767 + compare: function(actual, expected) {
  2768 + if (!j$.isString_(expected) && !j$.isA_('RegExp', expected)) {
  2769 + throw new Error('Expected is not a String or a RegExp');
  2770 + }
  2771 +
  2772 + var regexp = new RegExp(expected);
  2773 +
  2774 + return {
  2775 + pass: regexp.test(actual)
  2776 + };
  2777 + }
  2778 + };
  2779 + }
  2780 +
  2781 + return toMatch;
  2782 +};
  2783 +
  2784 +getJasmineRequireObj().toThrow = function(j$) {
  2785 +
  2786 + function toThrow(util) {
  2787 + return {
  2788 + compare: function(actual, expected) {
  2789 + var result = { pass: false },
  2790 + threw = false,
  2791 + thrown;
  2792 +
  2793 + if (typeof actual != 'function') {
  2794 + throw new Error('Actual is not a Function');
  2795 + }
  2796 +
  2797 + try {
  2798 + actual();
  2799 + } catch (e) {
  2800 + threw = true;
  2801 + thrown = e;
  2802 + }
  2803 +
  2804 + if (!threw) {
  2805 + result.message = 'Expected function to throw an exception.';
  2806 + return result;
  2807 + }
  2808 +
  2809 + if (arguments.length == 1) {
  2810 + result.pass = true;
  2811 + result.message = function() { return 'Expected function not to throw, but it threw ' + j$.pp(thrown) + '.'; };
  2812 +
  2813 + return result;
  2814 + }
  2815 +
  2816 + if (util.equals(thrown, expected)) {
  2817 + result.pass = true;
  2818 + result.message = function() { return 'Expected function not to throw ' + j$.pp(expected) + '.'; };
  2819 + } else {
  2820 + result.message = function() { return 'Expected function to throw ' + j$.pp(expected) + ', but it threw ' + j$.pp(thrown) + '.'; };
  2821 + }
  2822 +
  2823 + return result;
  2824 + }
  2825 + };
  2826 + }
  2827 +
  2828 + return toThrow;
  2829 +};
  2830 +
  2831 +getJasmineRequireObj().toThrowError = function(j$) {
  2832 + function toThrowError (util) {
  2833 + return {
  2834 + compare: function(actual) {
  2835 + var threw = false,
  2836 + pass = {pass: true},
  2837 + fail = {pass: false},
  2838 + thrown;
  2839 +
  2840 + if (typeof actual != 'function') {
  2841 + throw new Error('Actual is not a Function');
  2842 + }
  2843 +
  2844 + var errorMatcher = getMatcher.apply(null, arguments);
  2845 +
  2846 + try {
  2847 + actual();
  2848 + } catch (e) {
  2849 + threw = true;
  2850 + thrown = e;
  2851 + }
  2852 +
  2853 + if (!threw) {
  2854 + fail.message = 'Expected function to throw an Error.';
  2855 + return fail;
  2856 + }
  2857 +
  2858 + if (!(thrown instanceof Error)) {
  2859 + fail.message = function() { return 'Expected function to throw an Error, but it threw ' + j$.pp(thrown) + '.'; };
  2860 + return fail;
  2861 + }
  2862 +
  2863 + if (errorMatcher.hasNoSpecifics()) {
  2864 + pass.message = 'Expected function not to throw an Error, but it threw ' + j$.fnNameFor(thrown) + '.';
  2865 + return pass;
  2866 + }
  2867 +
  2868 + if (errorMatcher.matches(thrown)) {
  2869 + pass.message = function() {
  2870 + return 'Expected function not to throw ' + errorMatcher.errorTypeDescription + errorMatcher.messageDescription() + '.';
  2871 + };
  2872 + return pass;
  2873 + } else {
  2874 + fail.message = function() {
  2875 + return 'Expected function to throw ' + errorMatcher.errorTypeDescription + errorMatcher.messageDescription() +
  2876 + ', but it threw ' + errorMatcher.thrownDescription(thrown) + '.';
  2877 + };
  2878 + return fail;
  2879 + }
  2880 + }
  2881 + };
  2882 +
  2883 + function getMatcher() {
  2884 + var expected = null,
  2885 + errorType = null;
  2886 +
  2887 + if (arguments.length == 2) {
  2888 + expected = arguments[1];
  2889 + if (isAnErrorType(expected)) {
  2890 + errorType = expected;
  2891 + expected = null;
  2892 + }
  2893 + } else if (arguments.length > 2) {
  2894 + errorType = arguments[1];
  2895 + expected = arguments[2];
  2896 + if (!isAnErrorType(errorType)) {
  2897 + throw new Error('Expected error type is not an Error.');
  2898 + }
  2899 + }
  2900 +
  2901 + if (expected && !isStringOrRegExp(expected)) {
  2902 + if (errorType) {
  2903 + throw new Error('Expected error message is not a string or RegExp.');
  2904 + } else {
  2905 + throw new Error('Expected is not an Error, string, or RegExp.');
  2906 + }
  2907 + }
  2908 +
  2909 + function messageMatch(message) {
  2910 + if (typeof expected == 'string') {
  2911 + return expected == message;
  2912 + } else {
  2913 + return expected.test(message);
  2914 + }
  2915 + }
  2916 +
  2917 + return {
  2918 + errorTypeDescription: errorType ? j$.fnNameFor(errorType) : 'an exception',
  2919 + thrownDescription: function(thrown) {
  2920 + var thrownName = errorType ? j$.fnNameFor(thrown.constructor) : 'an exception',
  2921 + thrownMessage = '';
  2922 +
  2923 + if (expected) {
  2924 + thrownMessage = ' with message ' + j$.pp(thrown.message);
  2925 + }
  2926 +
  2927 + return thrownName + thrownMessage;
  2928 + },
  2929 + messageDescription: function() {
  2930 + if (expected === null) {
  2931 + return '';
  2932 + } else if (expected instanceof RegExp) {
  2933 + return ' with a message matching ' + j$.pp(expected);
  2934 + } else {
  2935 + return ' with message ' + j$.pp(expected);
  2936 + }
  2937 + },
  2938 + hasNoSpecifics: function() {
  2939 + return expected === null && errorType === null;
  2940 + },
  2941 + matches: function(error) {
  2942 + return (errorType === null || error.constructor === errorType) &&
  2943 + (expected === null || messageMatch(error.message));
  2944 + }
  2945 + };
  2946 + }
  2947 +
  2948 + function isStringOrRegExp(potential) {
  2949 + return potential instanceof RegExp || (typeof potential == 'string');
  2950 + }
  2951 +
  2952 + function isAnErrorType(type) {
  2953 + if (typeof type !== 'function') {
  2954 + return false;
  2955 + }
  2956 +
  2957 + var Surrogate = function() {};
  2958 + Surrogate.prototype = type.prototype;
  2959 + return (new Surrogate()) instanceof Error;
  2960 + }
  2961 + }
  2962 +
  2963 + return toThrowError;
  2964 +};
  2965 +
  2966 +getJasmineRequireObj().interface = function(jasmine, env) {
  2967 + var jasmineInterface = {
  2968 + describe: function(description, specDefinitions) {
  2969 + return env.describe(description, specDefinitions);
  2970 + },
  2971 +
  2972 + xdescribe: function(description, specDefinitions) {
  2973 + return env.xdescribe(description, specDefinitions);
  2974 + },
  2975 +
  2976 + fdescribe: function(description, specDefinitions) {
  2977 + return env.fdescribe(description, specDefinitions);
  2978 + },
  2979 +
  2980 + it: function() {
  2981 + return env.it.apply(env, arguments);
  2982 + },
  2983 +
  2984 + xit: function() {
  2985 + return env.xit.apply(env, arguments);
  2986 + },
  2987 +
  2988 + fit: function() {
  2989 + return env.fit.apply(env, arguments);
  2990 + },
  2991 +
  2992 + beforeEach: function() {
  2993 + return env.beforeEach.apply(env, arguments);
  2994 + },
  2995 +
  2996 + afterEach: function() {
  2997 + return env.afterEach.apply(env, arguments);
  2998 + },
  2999 +
  3000 + beforeAll: function() {
  3001 + return env.beforeAll.apply(env, arguments);
  3002 + },
  3003 +
  3004 + afterAll: function() {
  3005 + return env.afterAll.apply(env, arguments);
  3006 + },
  3007 +
  3008 + expect: function(actual) {
  3009 + return env.expect(actual);
  3010 + },
  3011 +
  3012 + pending: function() {
  3013 + return env.pending.apply(env, arguments);
  3014 + },
  3015 +
  3016 + fail: function() {
  3017 + return env.fail.apply(env, arguments);
  3018 + },
  3019 +
  3020 + spyOn: function(obj, methodName) {
  3021 + return env.spyOn(obj, methodName);
  3022 + },
  3023 +
  3024 + jsApiReporter: new jasmine.JsApiReporter({
  3025 + timer: new jasmine.Timer()
  3026 + }),
  3027 +
  3028 + jasmine: jasmine
  3029 + };
  3030 +
  3031 + jasmine.addCustomEqualityTester = function(tester) {
  3032 + env.addCustomEqualityTester(tester);
  3033 + };
  3034 +
  3035 + jasmine.addMatchers = function(matchers) {
  3036 + return env.addMatchers(matchers);
  3037 + };
  3038 +
  3039 + jasmine.clock = function() {
  3040 + return env.clock;
  3041 + };
  3042 +
  3043 + return jasmineInterface;
  3044 +};
  3045 +
  3046 +getJasmineRequireObj().version = function() {
  3047 + return '2.2.0';
  3048 +};
... ...
test/lib/jasmine-2.2.0/jasmine_favicon.png 0 → 100644

1.45 KB

test/lib/requirejs-plugins/domReady.js 0 → 100644
... ... @@ -0,0 +1,129 @@
  1 +/**
  2 + * @license RequireJS domReady 2.0.1 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved.
  3 + * Available via the MIT or new BSD license.
  4 + * see: http://github.com/requirejs/domReady for details
  5 + */
  6 +/*jslint */
  7 +/*global require: false, define: false, requirejs: false,
  8 + window: false, clearInterval: false, document: false,
  9 + self: false, setInterval: false */
  10 +
  11 +
  12 +define(function () {
  13 + 'use strict';
  14 +
  15 + var isTop, testDiv, scrollIntervalId,
  16 + isBrowser = typeof window !== "undefined" && window.document,
  17 + isPageLoaded = !isBrowser,
  18 + doc = isBrowser ? document : null,
  19 + readyCalls = [];
  20 +
  21 + function runCallbacks(callbacks) {
  22 + var i;
  23 + for (i = 0; i < callbacks.length; i += 1) {
  24 + callbacks[i](doc);
  25 + }
  26 + }
  27 +
  28 + function callReady() {
  29 + var callbacks = readyCalls;
  30 +
  31 + if (isPageLoaded) {
  32 + //Call the DOM ready callbacks
  33 + if (callbacks.length) {
  34 + readyCalls = [];
  35 + runCallbacks(callbacks);
  36 + }
  37 + }
  38 + }
  39 +
  40 + /**
  41 + * Sets the page as loaded.
  42 + */
  43 + function pageLoaded() {
  44 + if (!isPageLoaded) {
  45 + isPageLoaded = true;
  46 + if (scrollIntervalId) {
  47 + clearInterval(scrollIntervalId);
  48 + }
  49 +
  50 + callReady();
  51 + }
  52 + }
  53 +
  54 + if (isBrowser) {
  55 + if (document.addEventListener) {
  56 + //Standards. Hooray! Assumption here that if standards based,
  57 + //it knows about DOMContentLoaded.
  58 + document.addEventListener("DOMContentLoaded", pageLoaded, false);
  59 + window.addEventListener("load", pageLoaded, false);
  60 + } else if (window.attachEvent) {
  61 + window.attachEvent("onload", pageLoaded);
  62 +
  63 + testDiv = document.createElement('div');
  64 + try {
  65 + isTop = window.frameElement === null;
  66 + } catch (e) {}
  67 +
  68 + //DOMContentLoaded approximation that uses a doScroll, as found by
  69 + //Diego Perini: http://javascript.nwbox.com/IEContentLoaded/,
  70 + //but modified by other contributors, including jdalton
  71 + if (testDiv.doScroll && isTop && window.external) {
  72 + scrollIntervalId = setInterval(function () {
  73 + try {
  74 + testDiv.doScroll();
  75 + pageLoaded();
  76 + } catch (e) {}
  77 + }, 30);
  78 + }
  79 + }
  80 +
  81 + //Check if document already complete, and if so, just trigger page load
  82 + //listeners. Latest webkit browsers also use "interactive", and
  83 + //will fire the onDOMContentLoaded before "interactive" but not after
  84 + //entering "interactive" or "complete". More details:
  85 + //http://dev.w3.org/html5/spec/the-end.html#the-end
  86 + //http://stackoverflow.com/questions/3665561/document-readystate-of-interactive-vs-ondomcontentloaded
  87 + //Hmm, this is more complicated on further use, see "firing too early"
  88 + //bug: https://github.com/requirejs/domReady/issues/1
  89 + //so removing the || document.readyState === "interactive" test.
  90 + //There is still a window.onload binding that should get fired if
  91 + //DOMContentLoaded is missed.
  92 + if (document.readyState === "complete") {
  93 + pageLoaded();
  94 + }
  95 + }
  96 +
  97 + /** START OF PUBLIC API **/
  98 +
  99 + /**
  100 + * Registers a callback for DOM ready. If DOM is already ready, the
  101 + * callback is called immediately.
  102 + * @param {Function} callback
  103 + */
  104 + function domReady(callback) {
  105 + if (isPageLoaded) {
  106 + callback(doc);
  107 + } else {
  108 + readyCalls.push(callback);
  109 + }
  110 + return domReady;
  111 + }
  112 +
  113 + domReady.version = '2.0.1';
  114 +
  115 + /**
  116 + * Loader Plugin API method
  117 + */
  118 + domReady.load = function (name, req, onLoad, config) {
  119 + if (config.isBuild) {
  120 + onLoad(null);
  121 + } else {
  122 + domReady(onLoad);
  123 + }
  124 + };
  125 +
  126 + /** END OF PUBLIC API **/
  127 +
  128 + return domReady;
  129 +});
... ...
test/spec/MainSpec.js 0 → 100644
... ... @@ -0,0 +1,16 @@
  1 +define(['jasmine-boot', 'model/proposal-app'],function(jasmine, ProposalApp){
  2 +
  3 + describe("Limit proposal voting to one per browser", function(){
  4 + it("Should register a proposal id", function(){
  5 + expect(ProposalApp.addVotedProposal(99)).toContain(99);
  6 + });
  7 +
  8 + it("Should find a voted proposal", function(){
  9 + ProposalApp.addVotedProposal(3);
  10 + expect(ProposalApp.hasProposalbeenVoted(3)).toBe(true);
  11 + });
  12 +
  13 + });
  14 +
  15 +
  16 +});
... ...