<commit_before>/**
 * MISSKEY BOOT LOADER
 * (ENTRY POINT)
 */

/**
 * ドメインに基づいて適切なスクリプトを読み込みます。
 * ユーザーの言語およびモバイル端末か否かも考慮します。
 * webpackは介さないためrequireやimportは使えません。
 */

'use strict';

// Chromeで確認したことなのですが、constやletを用いたとしても
// グローバルなスコープで定数/変数を定義するとwindowのプロパティ
// としてそれがアクセスできるようになる訳ではありませんが、普通に
// コンソールから定数/変数名を入力するとアクセスできてしまいます。
// ブロック内に入れてスコープを非グローバル化するとそれが防げます
// (Chrome以外のブラウザでは検証していません)
{
	// Get the current url information
	const url = new URL(location.href);

	// Extarct the (sub) domain part of the current url
	//
	// e.g.
	//   misskey.alice               => misskey
	//   misskey.strawberry.pasta    => misskey
	//   dev.misskey.arisu.tachibana => dev
	let app = url.host.split('.')[0];

	// Detect the user language
	// Note: The default language is English
	let lang = navigator.language.split('-')[0];
	if (!/^(en|ja)$/.test(lang)) lang = 'en';

	// Detect the user agent
	const ua = navigator.userAgent.toLowerCase();
	const isMobile = /mobile|iphone|ipad|android/.test(ua);

	// Get the <head> element
	const head = document.getElementsByTagName('head')[0];

	// If mobile, insert the viewport meta tag
	if (isMobile) {
		const meta = document.createElement('meta');
		meta.setAttribute('name', 'viewport');
		meta.setAttribute('content', [
			['width', 'device-width'],
			['initial-scale', '1'],
			['minimum-scale', '1'],
			['maximum-scale', '1'],
			['user-scalable', 'no']
		].map(x => x.join('=')).join(','));
		head.appendChild(meta);
	}

	// Switch desktop or mobile version
	if (app == 'misskey') {
		app = isMobile ? 'mobile' : 'desktop';
	}

	// Load an app script
	// Note: 'async' make it possible to load the script asyncly.
	//       'defer' make it possible to run the script when the dom loaded.
	const script = document.createElement('script');
	script.setAttribute('src', `/assets/${app}.${VERSION}.${lang}.js`);
	script.setAttribute('async', 'true');
	script.setAttribute('defer', 'true');
	head.appendChild(script);
}
<commit_msg>Refactor<commit_after>/**
 * MISSKEY BOOT LOADER
 * (ENTRY POINT)
 */

/**
 * ドメインに基づいて適切なスクリプトを読み込みます。
 * ユーザーの言語およびモバイル端末か否かも考慮します。
 * webpackは介さないためrequireやimportは使えません。
 */

'use strict';

// Chromeで確認したことなのですが、constやletを用いたとしても
// グローバルなスコープで定数/変数を定義するとwindowのプロパティ
// としてそれがアクセスできるようになる訳ではありませんが、普通に
// コンソールから定数/変数名を入力するとアクセスできてしまいます。
// ブロック内に入れてスコープを非グローバル化するとそれが防げます
// (Chrome以外のブラウザでは検証していません)
{
	// Get the current url information
	const url = new URL(location.href);

	// Extarct the (sub) domain part of the current url
	//
	// e.g.
	//   misskey.alice               => misskey
	//   misskey.strawberry.pasta    => misskey
	//   dev.misskey.arisu.tachibana => dev
	let app = url.host.split('.')[0];

	// Detect the user language
	// Note: The default language is English
	let lang = navigator.language.split('-')[0];
	if (!/^(en|ja)$/.test(lang)) lang = 'en';

	// Detect the user agent
	const ua = navigator.userAgent.toLowerCase();
	const isMobile = /mobile|iphone|ipad|android/.test(ua);

	// Get the <head> element
	const head = document.getElementsByTagName('head')[0];

	// If mobile, insert the viewport meta tag
	if (isMobile) {
		const meta = document.createElement('meta');
		meta.setAttribute('name', 'viewport');
		meta.setAttribute('content', Object.entries({
			'width': 'device-width',
			'initial-scale': '1',
			'minimum-scale': '1',
			'maximum-scale': '1',
			'user-scalable': 'no'
		}).map(x => x.join('=')).join(','));
		head.appendChild(meta);
	}

	// Switch desktop or mobile version
	if (app == 'misskey') {
		app = isMobile ? 'mobile' : 'desktop';
	}

	// Load an app script
	// Note: 'async' make it possible to load the script asyncly.
	//       'defer' make it possible to run the script when the dom loaded.
	const script = document.createElement('script');
	script.setAttribute('src', `/assets/${app}.${VERSION}.${lang}.js`);
	script.setAttribute('async', 'true');
	script.setAttribute('defer', 'true');
	head.appendChild(script);
}
<|endoftext|>


<commit_before>
export const wrapperStyle = {
    position: 'fixed',
    bottom: '0px',
    right: '4px',
    zIndex: 2147483647,
    borderRadius: '5px',
    border: '1px solid rgba(82,179,217,0.9)',
    width: '300px',
    background: 'rgb(229, 229, 229)',
    boxSizing: 'content-box'
};

export const mobileOpenWrapperStyle = {
    position: 'fixed',
    top: 0,
    right: 0,
    bottom: 0,
    left: 0,
    zIndex: 2147483647,
    width: '100%',
    height: '100%',
    background: 'rgb(229, 229, 229)',
    overflowY: 'visible '
};

export const titleStyle = {
    height: '30px',
    lineHeight: '30px',
    fontSize: '20px',
    display: 'flex',
    justifyContent: 'space-between',
    paddingLeft: '10px',
    fontFamily: 'Lato, sans-serif',
    background: 'rgba(82,179,217,0.9)',
    color: '#fff',
    cursor: 'pointer'
};<commit_msg>box-sizing: content-box - at wrapper level to override external style (also on mobile)<commit_after>
export const wrapperStyle = {
    position: 'fixed',
    bottom: '0px',
    right: '4px',
    zIndex: 2147483647,
    borderRadius: '5px',
    border: '1px solid rgba(82,179,217,0.9)',
    width: '300px',
    background: 'rgb(229, 229, 229)',
    boxSizing: 'content-box'
};

export const mobileOpenWrapperStyle = {
    position: 'fixed',
    top: 0,
    right: 0,
    bottom: 0,
    left: 0,
    zIndex: 2147483647,
    width: '100%',
    height: '100%',
    background: 'rgb(229, 229, 229)',
    overflowY: 'visible',
    boxSizing: 'content-box'
};

export const titleStyle = {
    height: '30px',
    lineHeight: '30px',
    fontSize: '20px',
    display: 'flex',
    justifyContent: 'space-between',
    paddingLeft: '10px',
    fontFamily: 'Lato, sans-serif',
    background: 'rgba(82,179,217,0.9)',
    color: '#fff',
    cursor: 'pointer'
};<|endoftext|>


<commit_before>const MyWorker = require(
    process.env.NODE_ENV === 'production'
        ? 'worker-loader?name=worker.[chunkhash:6].js!./worker.js'
        : 'worker-loader?name=worker.js!./worker.js'
)

module.exports = function makeWorker (callback) {
    const worker = new MyWorker()
    worker.onmessage = function (event) {
        const [method, data] = event.data
        callback(method, data)
    }
    worker.post = function (method, data) {
        worker.postMessage([method, data])
    }
    return worker
}
<commit_msg>Use inline web worker<commit_after>const MyWorker = require(
    process.env.NODE_ENV === 'production'
        ? 'worker-loader?name=worker.[chunkhash:6].js&inline!./worker.js'
        : 'worker-loader?name=worker.js!./worker.js'
)

module.exports = function makeWorker (callback) {
    const worker = new MyWorker()
    worker.onmessage = function (event) {
        const [method, data] = event.data
        callback(method, data)
    }
    worker.post = function (method, data) {
        worker.postMessage([method, data])
    }
    return worker
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2014, EDINA.
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this
   list of conditions and the following disclaimer in the documentation and/or
   other materials provided with the distribution.
3. All advertising materials mentioning features or use of this software must
   display the following acknowledgement: This product includes software
   developed by the EDINA.
4. Neither the name of the EDINA nor the names of its contributors may be used to
   endorse or promote products derived from this software without specific prior
   written permission.

THIS SOFTWARE IS PROVIDED BY EDINA ''AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL EDINA BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
*/

"use strict";

/**
 * TODO
 */
define(['utils', './pcapi'], function(utils, pcapi){
    /**
     * Get the cloud login from local storage.
     */
    var getCloudLogin = function(){
        var login = null;
        var user = localStorage.getItem('cloud-user');
        if(user){
            login = JSON.parse(user);
        }

        return login;
    };

    /**
     * Get the cloud login id from local storage.
     */
    var getCloudLoginId = function(){
        var id;
        var login = getCloudLogin();
        if(login){
            id = login.id;
        }

        return id;
    };

    /**
     * Unset user login id.
     */
    var clearCloudLogin = function(){
        localStorage.setItem('cloud-user', JSON.stringify({'id': undefined}));
    };

    /**
     * Login to cloud provider.
     * @paran provider The provider type.
     * @param callback Function called after login attempt.
     * @param cbrowser Function to allow caller requires access to childbrowser.
     */
    var doLogin = function(provider, callback, cbrowser){
        var loginUrl = pcapi.getCloudProviderUrl() + '/auth/' + provider;
        if (provider === 'local') {
            doLoginLocal(callback, loginUrl);
        }
        else{
            doLoginDropBox(callback, cbrowser, loginUrl);
        }
    };

    /**
     * Login to a local cloud provider.
     * @param callback Function called after login attempt.
     * @param loginUrl
     */
    var doLoginLocal = function(callback, loginUrl){
        $('#login-form').toggle();
        $(document).off('vmousedown', '#local-login');
        $(document).on('vmousedown', '#local-login', function(event){
            loginUrl += '/' + $("#login-username").val();
            console.debug('Login with: ' + loginUrl + '?async=true');

            $.ajax({
                url: loginUrl + '?async=true',
                timeout: 3000,
                cache: false,
                success: function(data){
                    console.debug("Redirect to: " + data.url);
                    var cloudUserId = data.userid;
                    _this.setCloudLogin(cloudUserId);
                    callback(cloudUserId);
                }
            });
        });
    };

    /**
     * Login to dropbox.
     * @param callback Function called after login attempt.
     * @param cbrowser Function to allow caller requires access to childbrowser.
     * @param loginUrl
     */
    var doLoginDropBox = function(callback, cbrowser, loginUrl){
        var pollTimer, pollTimerCount = 0, pollInterval = 3000, pollForMax = 5 * 60 * 1000; //min

        var userId = getCloudLoginId();
        if(userId !== undefined){
            console.debug("got a user id: " + userId);
            loginUrl += '/' + userId;
        }

        // clear user id
        clearCloudLogin();
        console.debug('Login with: ' + loginUrl + '?async=true');

        $.ajax({
            url: loginUrl + '?async=true',
            timeout: 3000,
            cache: false,
            success: function(data){
                console.debug("Redirect to: " + data.url);
                var cloudUserId = data.userid;

                // close child browser
                var closeCb = function(userId){
                    clearInterval(pollTimer);
                    callback(userId);
                };

                // open dropbox login in child browser
                var cb = window.open(data.url, '_blank', 'location=no');
                //cb.addEventListener('exit', closeCb);

                var pollUrl = loginUrl + '/' + cloudUserId + '?async=true';
                console.debug('Poll: ' + pollUrl);
                pollTimer = setInterval(function(){
                    $.ajax({
                        url: pollUrl,
                        success: function(pollData){
                            pollTimerCount += pollInterval;

                            if(pollData.state === 1 || pollTimerCount > pollForMax){
                                if(pollData.state === 1 ){
                                    _this.setCloudLogin(cloudUserId);
                                }
                                cb.close();
                                closeCb(cloudUserId);
                            }
                        },
                        error: function(error){
                            console.error("Problem polling api: " + error.statusText);
                            closeCb(-1);
                        },
                        cache: false
                    });
                }, pollInterval);

                if(cbrowser){
                    // caller may want access to child browser reference
                    cbrowser(cb);
                }
            },
            error: function(jqXHR, textStatus){
                var msg;
                if(textStatus === undefined){
                    textStatus = ' Unspecified Error.';
                }
                else if(textStatus === "timeout") {
                    msg = "Unable to login, please enable data connection.";
                }
                else{
                    msg = "Problem with login: " + textStatus;
                }

                utils.printObj(jqXHR);
                console.error(msg);
                utils.inform(msg);
            }
        });
    };

var _this = {

    /**
     * Check if users session is valid.
     * @param TODO
     */
    checkLogin: function(callback){
        if(!this.userId){
            var user = getCloudLogin();
            if(user !== null && user.id){
                var url = pcapi.getCloudProviderUrl() + '/auth/'+pcapi.getProvider()+'/' + user.id;
                console.debug("Check user with: " + url);
                $.ajax({
                    type: 'GET',
                    dataType: 'json',
                    url: url,
                    cache: false,
                    success: $.proxy(function(data){
                        if(data.state === 1){
                            this.setCloudLogin(user.id, user.cursor);
                        }

                        callback(user.id);
                    }, this),
                    error: $.proxy(function(error){
                        console.error("Error with user: " + url + " : " + error.msg);
                        this.logoutCloud();
                    }, this)
                });
            }
            else{
                console.debug("No user session saved");
                this.logoutCloud();
            }
        }
        else{
            callback(this.userId);
        }
    },

    /**
     * @return The cloud login user.
     *   id - cloud user id
     *   cursor - cursor of last sync.
     */
    getUser: function(){
        return this.user;
    },

    /**
     * Login to cloud provider.
     */
    loginCloud: function(provider, cb, cbrowser){
        doLogin(provider, cb, cbrowser);
    },

    /**
     * Login to cloud provider.
     */
    logoutCloud: function(){
        this.user = undefined;
        clearCloudLogin();
    },

    /**
     * Store cloud user id in local storage.
     */
    setCloudLogin: function(userId, cursor){
        this.user = {
            'id': userId,
            'cursor': cursor
        };

        localStorage.setItem('cloud-user', JSON.stringify(this.user));
    }
};

return _this;

});
<commit_msg>Handle the non-authorized status of the pcapi<commit_after>/*
Copyright (c) 2014, EDINA.
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this
   list of conditions and the following disclaimer in the documentation and/or
   other materials provided with the distribution.
3. All advertising materials mentioning features or use of this software must
   display the following acknowledgement: This product includes software
   developed by the EDINA.
4. Neither the name of the EDINA nor the names of its contributors may be used to
   endorse or promote products derived from this software without specific prior
   written permission.

THIS SOFTWARE IS PROVIDED BY EDINA ''AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL EDINA BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
*/

"use strict";

/**
 * TODO
 */
define(['utils', './pcapi'], function(utils, pcapi){
    /**
     * Get the cloud login from local storage.
     */
    var getCloudLogin = function(){
        var login = null;
        var user = localStorage.getItem('cloud-user');
        if(user){
            login = JSON.parse(user);
        }

        return login;
    };

    /**
     * Get the cloud login id from local storage.
     */
    var getCloudLoginId = function(){
        var id;
        var login = getCloudLogin();
        if(login){
            id = login.id;
        }

        return id;
    };

    /**
     * Unset user login id.
     */
    var clearCloudLogin = function(){
        localStorage.setItem('cloud-user', JSON.stringify({'id': undefined}));
    };

    /**
     * Login to cloud provider.
     * @paran provider The provider type.
     * @param callback Function called after login attempt.
     * @param cbrowser Function to allow caller requires access to childbrowser.
     */
    var doLogin = function(provider, callback, cbrowser){
        var loginUrl = pcapi.getCloudProviderUrl() + '/auth/' + provider;
        if (provider === 'local') {
            doLoginLocal(callback, loginUrl);
        }
        else{
            doLoginDropBox(callback, cbrowser, loginUrl);
        }
    };

    /**
     * Login to a local cloud provider.
     * @param callback Function called after login attempt.
     * @param loginUrl
     */
    var doLoginLocal = function(callback, loginUrl){
        $('#login-form').toggle();
        $(document).off('vmousedown', '#local-login');
        $(document).on('vmousedown', '#local-login', function(event){
            loginUrl += '/' + $("#login-username").val();
            console.debug('Login with: ' + loginUrl + '?async=true');

            $.ajax({
                url: loginUrl + '?async=true',
                timeout: 3000,
                cache: false,
                success: function(data){
                    console.debug("Redirect to: " + data.url);
                    var cloudUserId = data.userid;
                    _this.setCloudLogin(cloudUserId);
                    callback(cloudUserId);
                }
            });
        });
    };

    /**
     * Login to dropbox.
     * @param callback Function called after login attempt.
     * @param cbrowser Function to allow caller requires access to childbrowser.
     * @param loginUrl
     */
    var doLoginDropBox = function(callback, cbrowser, loginUrl){
        var pollTimer, pollTimerCount = 0, pollInterval = 3000, pollForMax = 5 * 60 * 1000; //min

        var userId = getCloudLoginId();
        if(userId !== undefined){
            console.debug("got a user id: " + userId);
            loginUrl += '/' + userId;
        }

        // clear user id
        clearCloudLogin();
        console.debug('Login with: ' + loginUrl + '?async=true');

        $.ajax({
            url: loginUrl + '?async=true',
            timeout: 3000,
            cache: false,
            success: function(data){
                console.debug("Redirect to: " + data.url);
                var cloudUserId = data.userid;

                // close child browser
                var closeCb = function(userId){
                    clearInterval(pollTimer);
                    callback(userId);
                };

                // open dropbox login in child browser
                var cb = window.open(data.url, '_blank', 'location=no');
                //cb.addEventListener('exit', closeCb);

                var pollUrl = loginUrl + '/' + cloudUserId + '?async=true';
                console.debug('Poll: ' + pollUrl);
                pollTimer = setInterval(function(){
                    $.ajax({
                        url: pollUrl,
                        success: function(pollData){
                            var endPolling = false;
                            pollTimerCount += pollInterval;

                            switch(pollData.state){
                                case 0: // In progress
                                break;
                                case 1: // Authorized
                                    _this.setCloudLogin(cloudUserId);
                                    endPolling = true;
                                break;
                                case 2: // Non Authorized
                                    cloudUserId = undefined;
                                    endPolling = true;
                                break;
                            }

                            // Timeout or end polling
                            if(pollTimerCount > pollForMax || endPolling){
                                cb.close();
                                closeCb(cloudUserId);
                            }
                        },
                        error: function(error){
                            console.error("Problem polling api: " + error.statusText);
                            closeCb(undefined);
                        },
                        cache: false
                    });
                }, pollInterval);

                if(cbrowser){
                    // caller may want access to child browser reference
                    cbrowser(cb);
                }
            },
            error: function(jqXHR, textStatus){
                var msg;
                if(textStatus === undefined){
                    textStatus = ' Unspecified Error.';
                }
                else if(textStatus === "timeout") {
                    msg = "Unable to login, please enable data connection.";
                }
                else{
                    msg = "Problem with login: " + textStatus;
                }

                utils.printObj(jqXHR);
                console.error(msg);
                utils.inform(msg);
            }
        });
    };

var _this = {

    /**
     * Check if users session is valid.
     * @param TODO
     */
    checkLogin: function(callback){
        if(!this.userId){
            var user = getCloudLogin();
            if(user !== null && user.id){
                var url = pcapi.getCloudProviderUrl() + '/auth/'+pcapi.getProvider()+'/' + user.id;
                console.debug("Check user with: " + url);
                $.ajax({
                    type: 'GET',
                    dataType: 'json',
                    url: url,
                    cache: false,
                    success: $.proxy(function(data){
                        if(data.state === 1){
                            this.setCloudLogin(user.id, user.cursor);
                        }

                        callback(user.id);
                    }, this),
                    error: $.proxy(function(error){
                        console.error("Error with user: " + url + " : " + error.msg);
                        this.logoutCloud();
                    }, this)
                });
            }
            else{
                console.debug("No user session saved");
                this.logoutCloud();
            }
        }
        else{
            callback(this.userId);
        }
    },

    /**
     * @return The cloud login user.
     *   id - cloud user id
     *   cursor - cursor of last sync.
     */
    getUser: function(){
        return this.user;
    },

    /**
     * Login to cloud provider.
     */
    loginCloud: function(provider, cb, cbrowser){
        doLogin(provider, cb, cbrowser);
    },

    /**
     * Login to cloud provider.
     */
    logoutCloud: function(){
        this.user = undefined;
        clearCloudLogin();
    },

    /**
     * Store cloud user id in local storage.
     */
    setCloudLogin: function(userId, cursor){
        this.user = {
            'id': userId,
            'cursor': cursor
        };

        localStorage.setItem('cloud-user', JSON.stringify(this.user));
    }
};

return _this;

});
<|endoftext|>


<commit_before>// Generated on 2013-12-05 using generator-angular 0.4.0
'use strict';
var LIVERELOAD_PORT = 35729;
var lrSnippet = require('connect-livereload')({ port: LIVERELOAD_PORT });
var mountFolder = function (connect, dir) {
    return connect.static(require('path').resolve(dir));
};

// # Globbing
// for performance reasons we're only matching one level down:
// 'test/spec/{,*/}*.js'
// use this if you want to recursively match all subfolders:
// 'test/spec/**/*.js'

module.exports = function (grunt) {
    require('load-grunt-tasks')(grunt);
    require('time-grunt')(grunt);

    // configurable paths
    var yeomanConfig = {
        app: 'app',
        dist: 'dist'
    };

    try {
        yeomanConfig.app = require('./bower.json').appPath || yeomanConfig.app;
    } catch (e) {}

    grunt.initConfig({
        yeoman: yeomanConfig,
        watch: {
            compass: {
                files: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'],
                tasks: ['compass:server', 'autoprefixer']
            },
            styles: {
                files: ['<%= yeoman.app %>/styles/{,*/}*.css'],
                tasks: ['copy:styles', 'autoprefixer']
            },
            livereload: {
                options: {
                    livereload: LIVERELOAD_PORT
                },
                files: [
                    '<%= yeoman.app %>/{,*/}*.html',
                    '.tmp/styles/{,*/}*.css',
                    '{.tmp,<%= yeoman.app %>}/scripts/{,*/}*.js',
                    '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
                ]
            }
        },
        autoprefixer: {
            options: ['last 1 version'],
            dist: {
                files: [{
                    expand: true,
                    cwd: '.tmp/styles/',
                    src: '{,*/}*.css',
                    dest: '.tmp/styles/'
                }]
            }
        },
        connect: {
            options: {
                port: 9000,
                // Change this to '0.0.0.0' to access the server from outside.
                hostname: 'localhost'
            },
            livereload: {
                options: {
                    middleware: function (connect) {
                        return [
                            lrSnippet,
                            mountFolder(connect, '.tmp'),
                            mountFolder(connect, yeomanConfig.app)
                        ];
                    }
                }
            },
            test: {
                options: {
                    middleware: function (connect) {
                        return [
                            mountFolder(connect, '.tmp'),
                            mountFolder(connect, 'test')
                        ];
                    }
                }
            },
            dist: {
                options: {
                    middleware: function (connect) {
                        return [
                            mountFolder(connect, yeomanConfig.dist)
                        ];
                    }
                }
            }
        },
        open: {
            server: {
                url: 'http://localhost:<%= connect.options.port %>'
            }
        },
        clean: {
            dist: {
                files: [{
                    dot: true,
                    src: [
                        '.tmp',
                        '<%= yeoman.dist %>/*',
                        '!<%= yeoman.dist %>/.git*'
                    ]
                }]
            },
            server: '.tmp'
        },
        jshint: {
            options: {
                jshintrc: '.jshintrc'
            },
            all: [
                'Gruntfile.js',
                '<%= yeoman.app %>/scripts/{,*/}*.js',
                '../service/src/{,*/}*.js',
                '../service/test/{,*/}*.js',
                '../service/bin/*.js',
                '../service/config*'
            ]
        },
        compass: {
            options: {
                sassDir: '<%= yeoman.app %>/styles',
                cssDir: '<%= yeoman.app %>/styles',
                generatedImagesDir: '<%= yeoman.app %>/images/generated',
                imagesDir: '<%= yeoman.app %>/images',
                javascriptsDir: '<%= yeoman.app %>/scripts',
                fontsDir: '<%= yeoman.app %>/styles/fonts',
                importPath: '<%= yeoman.app %>/bower_components',
                httpImagesPath: '<%= yeoman.app %>/images',
                httpGeneratedImagesPath: '<%= yeoman.app %>/images/generated',
                httpFontsPath: '<%= yeoman.app %>/styles/fonts',
                relativeAssets: true
            },
            dist: {},
            server: {
                options: {
                    debugInfo: true
                }
            }
        },
        // not used since Uglify task does concat,
        // but still available if needed
        /*concat: {
            dist: {}
        },*/
        rev: {
            dist: {
                files: {
                    src: [
                        '<%= yeoman.dist %>/scripts/{,*/}*.js',
                        '<%= yeoman.dist %>/styles/{,*/}*.css',
                        '<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
                        '<%= yeoman.dist %>/styles/fonts/*'
                    ]
                }
            }
        },
        useminPrepare: {
            html: '<%= yeoman.app %>/index.html',
            options: {
                dest: '<%= yeoman.dist %>'
            }
        },
        usemin: {
            html: ['<%= yeoman.dist %>/{,*/}*.html'],
            css: ['<%= yeoman.dist %>/styles/{,*/}*.css'],
            options: {
                dirs: ['<%= yeoman.dist %>']
            }
        },
        imagemin: {
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= yeoman.app %>/images',
                    src: '{,*/}*.{png,jpg,jpeg}',
                    dest: '<%= yeoman.dist %>/images'
                }]
            }
        },
        svgmin: {
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= yeoman.app %>/images',
                    src: '{,*/}*.svg',
                    dest: '<%= yeoman.dist %>/images'
                }]
            }
        },
        cssmin: {
            // By default, your `index.html` <!-- Usemin Block --> will take care of
            // minification. This option is pre-configured if you do not wish to use
            // Usemin blocks.
            // dist: {
            //     files: {
            //         '<%= yeoman.dist %>/styles/main.css': [
            //             '.tmp/styles/{,*/}*.css',
            //             '<%= yeoman.app %>/styles/{,*/}*.css'
            //         ]
            //     }
            // }
        },
        htmlmin: {
            dist: {
                options: {
                    /*removeCommentsFromCDATA: true,
                    // https://github.com/yeoman/grunt-usemin/issues/44
                    //collapseWhitespace: true,
                    collapseBooleanAttributes: true,
                    removeAttributeQuotes: true,
                    removeRedundantAttributes: true,
                    useShortDoctype: true,
                    removeEmptyAttributes: true,
                    removeOptionalTags: true*/
                },
                files: [{
                    expand: true,
                    cwd: '<%= yeoman.app %>',
                    src: ['*.html', 'views/*.html', 'views/*/*.html'],
                    dest: '<%= yeoman.dist %>'
                }]
            }
        },
        // Put files not handled in other tasks here
        copy: {
            dist: {
                files: [{
                    expand: true,
                    dot: true,
                    cwd: '<%= yeoman.app %>',
                    dest: '<%= yeoman.dist %>',
                    src: [
                        '*.{ico,png,txt}',
                        '.htaccess',
                        'bower_components/**/*',
                        'images/{,*/}*.{gif,webp}',
                        'fonts/*'
                    ]
                }, {
                    expand: true,
                    cwd: '.tmp/images',
                    dest: '<%= yeoman.dist %>/images',
                    src: [
                        'generated/*'
                    ]
                }]
            },
            styles: {
                expand: true,
                cwd: '<%= yeoman.app %>/styles',
                dest: '.tmp/styles/',
                src: '{,*/}*.css'
            }
        },
        concurrent: {
            server: [
                'compass:server',
                'copy:styles'
            ],
            test: [
                'compass',
                'copy:styles'
            ],
            dist: [
                'compass:dist',
                'copy:styles',
                'imagemin',
                'svgmin',
                'htmlmin'
            ]
        },
        karma: {
            unit: {
                configFile: 'karma.conf.js',
                singleRun: true
            }
        },
        cdnify: {
            dist: {
                html: ['<%= yeoman.dist %>/*.html']
            }
        },
        ngmin: {
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= yeoman.dist %>/scripts',
                    src: '*.js',
                    dest: '<%= yeoman.dist %>/scripts'
                }]
            }
        },
        uglify: {
            dist: {
                files: {
                    '<%= yeoman.dist %>/scripts/scripts.js': [
                        '<%= yeoman.dist %>/scripts/scripts.js'
                    ]
                }
            }
        }
    });

    grunt.registerTask('server', function (target) {
        if (target === 'dist') {
            return grunt.task.run(['build', 'open', 'connect:dist:keepalive']);
        }

        grunt.task.run([
            'clean:server',
            'concurrent:server',
            'autoprefixer',
            'connect:livereload',
            'open',
            'watch'
        ]);
    });

    grunt.registerTask('test', [
        'clean:server',
        'concurrent:test',
        'autoprefixer',
        'connect:test',
        'karma'
    ]);

    grunt.registerTask('build', [
        'clean:dist',
        'useminPrepare',
        'concurrent:dist',
        'autoprefixer',
        'concat',
        'copy:dist',
        'cdnify',
        'ngmin',
        'cssmin',
        'uglify',
        'rev',
        'usemin'
    ]);

    grunt.registerTask('default', [
        /*'jshint',
        'test',*/
        'build'
    ]);
};
<commit_msg>It should be possible to debug from other hosts<commit_after>// Generated on 2013-12-05 using generator-angular 0.4.0
'use strict';
var LIVERELOAD_PORT = 35729;
var lrSnippet = require('connect-livereload')({ port: LIVERELOAD_PORT });
var mountFolder = function (connect, dir) {
    return connect.static(require('path').resolve(dir));
};

// # Globbing
// for performance reasons we're only matching one level down:
// 'test/spec/{,*/}*.js'
// use this if you want to recursively match all subfolders:
// 'test/spec/**/*.js'

module.exports = function (grunt) {
    require('load-grunt-tasks')(grunt);
    require('time-grunt')(grunt);

    // configurable paths
    var yeomanConfig = {
        app: 'app',
        dist: 'dist'
    };

    try {
        yeomanConfig.app = require('./bower.json').appPath || yeomanConfig.app;
    } catch (e) {}

    grunt.initConfig({
        yeoman: yeomanConfig,
        watch: {
            compass: {
                files: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'],
                tasks: ['compass:server', 'autoprefixer']
            },
            styles: {
                files: ['<%= yeoman.app %>/styles/{,*/}*.css'],
                tasks: ['copy:styles', 'autoprefixer']
            },
            livereload: {
                options: {
                    livereload: LIVERELOAD_PORT
                },
                files: [
                    '<%= yeoman.app %>/{,*/}*.html',
                    '.tmp/styles/{,*/}*.css',
                    '{.tmp,<%= yeoman.app %>}/scripts/{,*/}*.js',
                    '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
                ]
            }
        },
        autoprefixer: {
            options: ['last 1 version'],
            dist: {
                files: [{
                    expand: true,
                    cwd: '.tmp/styles/',
                    src: '{,*/}*.css',
                    dest: '.tmp/styles/'
                }]
            }
        },
        connect: {
            options: {
                port: 9000,
                // Change this to '0.0.0.0' to access the server from outside.
                hostname: '0.0.0.0'
            },
            livereload: {
                options: {
                    middleware: function (connect) {
                        return [
                            lrSnippet,
                            mountFolder(connect, '.tmp'),
                            mountFolder(connect, yeomanConfig.app)
                        ];
                    }
                }
            },
            test: {
                options: {
                    middleware: function (connect) {
                        return [
                            mountFolder(connect, '.tmp'),
                            mountFolder(connect, 'test')
                        ];
                    }
                }
            },
            dist: {
                options: {
                    middleware: function (connect) {
                        return [
                            mountFolder(connect, yeomanConfig.dist)
                        ];
                    }
                }
            }
        },
        open: {
            server: {
                url: 'http://localhost:<%= connect.options.port %>'
            }
        },
        clean: {
            dist: {
                files: [{
                    dot: true,
                    src: [
                        '.tmp',
                        '<%= yeoman.dist %>/*',
                        '!<%= yeoman.dist %>/.git*'
                    ]
                }]
            },
            server: '.tmp'
        },
        jshint: {
            options: {
                jshintrc: '.jshintrc'
            },
            all: [
                'Gruntfile.js',
                '<%= yeoman.app %>/scripts/{,*/}*.js',
                '../service/src/{,*/}*.js',
                '../service/test/{,*/}*.js',
                '../service/bin/*.js',
                '../service/config*'
            ]
        },
        compass: {
            options: {
                sassDir: '<%= yeoman.app %>/styles',
                cssDir: '<%= yeoman.app %>/styles',
                generatedImagesDir: '<%= yeoman.app %>/images/generated',
                imagesDir: '<%= yeoman.app %>/images',
                javascriptsDir: '<%= yeoman.app %>/scripts',
                fontsDir: '<%= yeoman.app %>/styles/fonts',
                importPath: '<%= yeoman.app %>/bower_components',
                httpImagesPath: '<%= yeoman.app %>/images',
                httpGeneratedImagesPath: '<%= yeoman.app %>/images/generated',
                httpFontsPath: '<%= yeoman.app %>/styles/fonts',
                relativeAssets: true
            },
            dist: {},
            server: {
                options: {
                    debugInfo: true
                }
            }
        },
        // not used since Uglify task does concat,
        // but still available if needed
        /*concat: {
            dist: {}
        },*/
        rev: {
            dist: {
                files: {
                    src: [
                        '<%= yeoman.dist %>/scripts/{,*/}*.js',
                        '<%= yeoman.dist %>/styles/{,*/}*.css',
                        '<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
                        '<%= yeoman.dist %>/styles/fonts/*'
                    ]
                }
            }
        },
        useminPrepare: {
            html: '<%= yeoman.app %>/index.html',
            options: {
                dest: '<%= yeoman.dist %>'
            }
        },
        usemin: {
            html: ['<%= yeoman.dist %>/{,*/}*.html'],
            css: ['<%= yeoman.dist %>/styles/{,*/}*.css'],
            options: {
                dirs: ['<%= yeoman.dist %>']
            }
        },
        imagemin: {
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= yeoman.app %>/images',
                    src: '{,*/}*.{png,jpg,jpeg}',
                    dest: '<%= yeoman.dist %>/images'
                }]
            }
        },
        svgmin: {
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= yeoman.app %>/images',
                    src: '{,*/}*.svg',
                    dest: '<%= yeoman.dist %>/images'
                }]
            }
        },
        cssmin: {
            // By default, your `index.html` <!-- Usemin Block --> will take care of
            // minification. This option is pre-configured if you do not wish to use
            // Usemin blocks.
            // dist: {
            //     files: {
            //         '<%= yeoman.dist %>/styles/main.css': [
            //             '.tmp/styles/{,*/}*.css',
            //             '<%= yeoman.app %>/styles/{,*/}*.css'
            //         ]
            //     }
            // }
        },
        htmlmin: {
            dist: {
                options: {
                    /*removeCommentsFromCDATA: true,
                    // https://github.com/yeoman/grunt-usemin/issues/44
                    //collapseWhitespace: true,
                    collapseBooleanAttributes: true,
                    removeAttributeQuotes: true,
                    removeRedundantAttributes: true,
                    useShortDoctype: true,
                    removeEmptyAttributes: true,
                    removeOptionalTags: true*/
                },
                files: [{
                    expand: true,
                    cwd: '<%= yeoman.app %>',
                    src: ['*.html', 'views/*.html', 'views/*/*.html'],
                    dest: '<%= yeoman.dist %>'
                }]
            }
        },
        // Put files not handled in other tasks here
        copy: {
            dist: {
                files: [{
                    expand: true,
                    dot: true,
                    cwd: '<%= yeoman.app %>',
                    dest: '<%= yeoman.dist %>',
                    src: [
                        '*.{ico,png,txt}',
                        '.htaccess',
                        'bower_components/**/*',
                        'images/{,*/}*.{gif,webp}',
                        'fonts/*'
                    ]
                }, {
                    expand: true,
                    cwd: '.tmp/images',
                    dest: '<%= yeoman.dist %>/images',
                    src: [
                        'generated/*'
                    ]
                }]
            },
            styles: {
                expand: true,
                cwd: '<%= yeoman.app %>/styles',
                dest: '.tmp/styles/',
                src: '{,*/}*.css'
            }
        },
        concurrent: {
            server: [
                'compass:server',
                'copy:styles'
            ],
            test: [
                'compass',
                'copy:styles'
            ],
            dist: [
                'compass:dist',
                'copy:styles',
                'imagemin',
                'svgmin',
                'htmlmin'
            ]
        },
        karma: {
            unit: {
                configFile: 'karma.conf.js',
                singleRun: true
            }
        },
        cdnify: {
            dist: {
                html: ['<%= yeoman.dist %>/*.html']
            }
        },
        ngmin: {
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= yeoman.dist %>/scripts',
                    src: '*.js',
                    dest: '<%= yeoman.dist %>/scripts'
                }]
            }
        },
        uglify: {
            dist: {
                files: {
                    '<%= yeoman.dist %>/scripts/scripts.js': [
                        '<%= yeoman.dist %>/scripts/scripts.js'
                    ]
                }
            }
        }
    });

    grunt.registerTask('server', function (target) {
        if (target === 'dist') {
            return grunt.task.run(['build', 'open', 'connect:dist:keepalive']);
        }

        grunt.task.run([
            'clean:server',
            'concurrent:server',
            'autoprefixer',
            'connect:livereload',
            'open',
            'watch'
        ]);
    });

    grunt.registerTask('test', [
        'clean:server',
        'concurrent:test',
        'autoprefixer',
        'connect:test',
        'karma'
    ]);

    grunt.registerTask('build', [
        'clean:dist',
        'useminPrepare',
        'concurrent:dist',
        'autoprefixer',
        'concat',
        'copy:dist',
        'cdnify',
        'ngmin',
        'cssmin',
        'uglify',
        'rev',
        'usemin'
    ]);

    grunt.registerTask('default', [
        /*'jshint',
        'test',*/
        'build'
    ]);
};
<|endoftext|>


<commit_before>rivets.formatters.temp = function(value) {
    return value + ' °C';
}
rivets.formatters.pressure = function(value) {
    return value + ' hPa';
}
rivets.formatters.rain = function(value) {
    return value + ' mm';
}
rivets.formatters.wind = function(value) {
    return value + ' kn';
}

var graph = function(el, json, attr, linecolorfunc, yaxisleg, width, height) {
    var margin = {top: 20, right: 20, bottom: 30, left: 50},
        width = width - margin.left - margin.right,
        height = height - margin.top - margin.bottom;

    var x = d3.time.scale()
        .range([0, width]);

    var y = d3.scale.linear()
        .range([height, 0]);

    var xAxis = d3.svg.axis()
        .scale(x)
        .tickFormat(d3.time.format("%H:%M"))
        .orient("bottom");

    var yAxis = d3.svg.axis()
        .scale(y)
        .orient("left");

    var colorscale2 = d3.scale.category20c();

    var line = d3.svg.line()
        .x(function(d) { return x(d.date); })
        .y(function(d) { return y(d.val); })
        .interpolate("basis")

    var svg = d3.select(el).append("svg")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
      .append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    json.forEach(function(d) {
        d.val = d[attr];
    });

    x.domain(d3.extent(json, function(d) { return d.date; }));
    y.domain(d3.extent(json, function(d) { return d.val; }));

    svg.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + height + ")")
      .call(xAxis);

    svg.append("g")
      .attr("class", "y axis")
      .call(yAxis)
    .append("text")
      .attr("transform", "rotate(-90)")
      .attr("y", 6)
      .attr("dy", ".71em")
      .style("text-anchor", "end")
      .text(yaxisleg);

    var yrule = svg.selectAll("g.y")
        .data(y.ticks(10))
        .enter().append("g")
        .attr("class", "y axis")
      .append("svg:line")
        .attr("class", "yLine")
        .style("stroke", "#eee")
        .style("shape-rendering", "crispEdges")
        .attr("x1", 0)
        .attr("x2", width)
        .attr("y1", y)
        .attr("y2", y);

    pathos = svg.append("path")
      .datum(json)
      .attr("class", "line")
      .attr("stroke", "darkred")
      .attr("d", line)

    pathos.each(function(d, i) {
        console.log(d, i);
    });

}

/* Configure Rivets to work with Watch JS */
rivets.configure({
  adapter: {
          subscribe: function(obj, keypath, callback) {
                    watch(obj, keypath, callback)
    },
    unsubscribe: function(obj, keypath, callback) {
              unwatch(obj, keypath, callback)
    },
    read: function(obj, keypath) {
              return obj[keypath]
    },
    publish: function(obj, keypath, value) {
              obj[keypath] = value
    }
  }
})

on_resize(function() {

    // Fetch new json data
    d3.json("/pg/", function(source) {
        /* First remove any existing svg */
        $('#main svg').remove();

        var colorscale = function(d, attr) {
            return "darkred";
            return "steelblue";
        };
        var parseDate = d3.time.format("%Y-%m-%d %H:%M:%S").parse;

        source.forEach(function(d) {
            d.date = parseDate(d.timestamp);
        });
        var width = $('#main').css('width').split('px')[0];
        var height = 180;

        graph('#temp', source, 'temp', colorscale, 'Temperature (°C)', width, height);
        graph('#pressure', source, 'barometer',colorscale,  'Air pressure (hPa)', width, height);
        graph('#wind', source, 'avg_speed', colorscale, 'Average wind speed (knot)', width, height);
        graph('#rain', source, 'daily_rain',colorscale,  'Daily rain (mm)', width, height);
        // Get the last element and populate rivets bindings with it
        rivets.bind(document.getElementById('main'), {current: source.slice(-1)[0]});
});
})(); // these parenthesis does the trick

// debulked onresize handler
function on_resize(c,t){onresize=function(){clearTimeout(t);t=setTimeout(c,100)};return c};
//
<commit_msg>fix API URL<commit_after>rivets.formatters.temp = function(value) {
    return value + ' °C';
}
rivets.formatters.pressure = function(value) {
    return value + ' hPa';
}
rivets.formatters.rain = function(value) {
    return value + ' mm';
}
rivets.formatters.wind = function(value) {
    return value + ' kn';
}

var graph = function(el, json, attr, linecolorfunc, yaxisleg, width, height) {
    var margin = {top: 20, right: 20, bottom: 30, left: 50},
        width = width - margin.left - margin.right,
        height = height - margin.top - margin.bottom;

    var x = d3.time.scale()
        .range([0, width]);

    var y = d3.scale.linear()
        .range([height, 0]);

    var xAxis = d3.svg.axis()
        .scale(x)
        .tickFormat(d3.time.format("%H:%M"))
        .orient("bottom");

    var yAxis = d3.svg.axis()
        .scale(y)
        .orient("left");

    var colorscale2 = d3.scale.category20c();

    var line = d3.svg.line()
        .x(function(d) { return x(d.date); })
        .y(function(d) { return y(d.val); })
        .interpolate("basis")

    var svg = d3.select(el).append("svg")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
      .append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    json.forEach(function(d) {
        d.val = d[attr];
    });

    x.domain(d3.extent(json, function(d) { return d.date; }));
    y.domain(d3.extent(json, function(d) { return d.val; }));

    svg.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + height + ")")
      .call(xAxis);

    svg.append("g")
      .attr("class", "y axis")
      .call(yAxis)
    .append("text")
      .attr("transform", "rotate(-90)")
      .attr("y", 6)
      .attr("dy", ".71em")
      .style("text-anchor", "end")
      .text(yaxisleg);

    var yrule = svg.selectAll("g.y")
        .data(y.ticks(10))
        .enter().append("g")
        .attr("class", "y axis")
      .append("svg:line")
        .attr("class", "yLine")
        .style("stroke", "#eee")
        .style("shape-rendering", "crispEdges")
        .attr("x1", 0)
        .attr("x2", width)
        .attr("y1", y)
        .attr("y2", y);

    pathos = svg.append("path")
      .datum(json)
      .attr("class", "line")
      .attr("stroke", "darkred")
      .attr("d", line)

    pathos.each(function(d, i) {
        console.log(d, i);
    });

}

/* Configure Rivets to work with Watch JS */
rivets.configure({
  adapter: {
          subscribe: function(obj, keypath, callback) {
                    watch(obj, keypath, callback)
    },
    unsubscribe: function(obj, keypath, callback) {
              unwatch(obj, keypath, callback)
    },
    read: function(obj, keypath) {
              return obj[keypath]
    },
    publish: function(obj, keypath, value) {
              obj[keypath] = value
    }
  }
})

on_resize(function() {

    // Fetch new json data
    d3.json("/api/", function(source) {
        /* First remove any existing svg */
        $('#main svg').remove();

        var colorscale = function(d, attr) {
            return "darkred";
            return "steelblue";
        };
        var parseDate = d3.time.format("%Y-%m-%d %H:%M:%S").parse;

        source.forEach(function(d) {
            d.date = parseDate(d.timestamp);
        });
        var width = $('#main').css('width').split('px')[0];
        var height = 180;

        graph('#temp', source, 'temp', colorscale, 'Temperature (°C)', width, height);
        graph('#pressure', source, 'barometer',colorscale,  'Air pressure (hPa)', width, height);
        graph('#wind', source, 'avg_speed', colorscale, 'Average wind speed (knot)', width, height);
        graph('#rain', source, 'daily_rain',colorscale,  'Daily rain (mm)', width, height);
        // Get the last element and populate rivets bindings with it
        rivets.bind(document.getElementById('main'), {current: source.slice(-1)[0]});
});
})(); // these parenthesis does the trick

// debulked onresize handler
function on_resize(c,t){onresize=function(){clearTimeout(t);t=setTimeout(c,100)};return c};
//
<|endoftext|>


<commit_before>Vue.component('ManifestView', {
  props: ['manifest', 'width'],
  template: `

{{ truncatedLabel }}

        View

`,
  computed: {
    columnWidth: function() {
      return `is-${this.width || 4}`;
    },
    viewerUrl: function() {
      return `/view/${this.manifest['@id'].split('/').slice(-2, 2)}`;
    },
    truncatedLabel: function() {
      if (this.manifest.label.length < 100) {
        return this.manifest.label;
      } else {
        var parts = this.manifest.label.split(" ");
        var label = "";
        while (label.length < 100) {
          var part = parts.shift();
          if ((label.length + part.length) > 100) {
            label += '…';
            break;
          }
          label += (" " + part);
        }
        return label;
      }
    }
  }
});
<commit_msg>ManifestView: Fix viewer link<commit_after>Vue.component('ManifestView', {
  props: ['manifest', 'width'],
  template: `

{{ truncatedLabel }}

        View

`,
  computed: {
    columnWidth: function() {
      return `is-${this.width || 4}`;
    },
    viewerUrl: function() {
      return `/view/${this.manifest['@id'].split('/').slice(-2)[0]}`;
    },
    truncatedLabel: function() {
      if (this.manifest.label.length < 100) {
        return this.manifest.label;
      } else {
        var parts = this.manifest.label.split(" ");
        var label = "";
        while (label.length < 100) {
          var part = parts.shift();
          if ((label.length + part.length) > 100) {
            label += '…';
            break;
          }
          label += (" " + part);
        }
        return label;
      }
    }
  }
});
<|endoftext|>


<commit_before>Paloquiz.states.Finish = function(game) {
    this.score = 0;
    this.hiscoresButton;
    this.hiscoresLabel;
    this.exitButton;
    this.exitLabel;
}

Paloquiz.states.Finish.prototype = {

    create: function() {
        getJSON('/status', function(gameStatus) {
            if (gameStatus['status'] !== 'finish') {
                this.status.start('Router');
                return;
            }
            getJSON('/summary', function(summary) {
                this.score = summary['score'];
                this.answers = summary['answers'];
                this.numQuestions = summary['num_questions'];
                this.createScoreText();
                this.createUI();
                fbLogIn(function() {
                    this.hiscoresButton.visible = true;
                    this.hiscoresLabel.visible = true;
                    fbGetUserScore(function(currentScore) {
                        if (this.score > currentScore) {
                            fbPublishScore(currentScore);
                        }
                    }, this);
                }, this);
            }, this);
        }, this);
    },

    shutdown: function() {},

    createScoreText: function() {;
        var scoreBaseTextSize = Math.round(.06 * this.world.height);
        var scoreTextColor = '#ff4f1a';

        var scoreHeader = this.add.text(
            this.world.centerX,
            this.world.height * .2,
            'Puntuación', {
                font: 'Pixel Art',
                fontSize: scoreBaseTextSize + 'px',
                fill: scoreTextColor,
                align: 'center'
            });
        scoreHeader.anchor.setTo(.5, 0);

        var scoreText = this.add.text(
            this.world.centerX,
            this.world.height * .33,
            this.score, {
                font: 'Pixel Art',
                fontSize: Math.round(scoreBaseTextSize * 2.2) + 'px',
                fill: scoreTextColor,
                align: 'center'
            });
        scoreText.anchor.setTo(.5, 0);
    },

    createUI: function() {
        // Hiscores
        this.hiscoresButton =
            this.add.button(this.world.centerX, this.world.height * .55,
                'finishButton',
                function() {
                    this.state.start('Highscores');
                }, this, 2, 1, 0);
        this.hiscoresButton.smoothed = false;
        this.hiscoresButton.width = this.world.width * .6;
        this.hiscoresButton.height = this.hiscoresButton.width * .3;
        this.hiscoresButton.anchor.setTo(.5, 0);
        this.hiscoresButton.visible = false;

        // Exit
        this.exitButton =
            this.add.button(this.hiscoresButton.x,
                this.hiscoresButton.y + this.hiscoresButton.height * 1.1,
                'finishButton',
                function() {
                    getJSON('/finish', function() {
                        this.state.start('Router');
                    }, this);
                }, this, 2, 1, 0);
        this.exitButton.smoothed = false;
        this.exitButton.width = this.hiscoresButton.width;
        this.exitButton.height = this.hiscoresButton.height;
        this.exitButton.anchor.setTo(.5, 0);

        // Labels
        var labelTextSize = Math.round(this.hiscoresButton.height / 3.0);
        var labelStyle = this.positionTextStyle = {
            font: 'Pixel Art',
            fontSize: labelTextSize + 'px',
            fill: 'white',
            align: 'center'
        };

        this.hiscoresLabel = this.add.text(
            this.world.centerX,
            this.hiscoresButton.y + this.hiscoresButton.height / 2,
            'Clasificación', labelStyle);
        this.hiscoresLabel.anchor.setTo(.5, .5);
        this.hiscoresLabel.visible = false;

        this.exitLabel = this.add.text(
            this.world.centerX,
            this.exitButton.y + this.exitButton.height / 2,
            'Salir', labelStyle);
        this.exitLabel.anchor.setTo(.5, .5);
    }
}<commit_msg>Improved hiscores button on finish<commit_after>Paloquiz.states.Finish = function(game) {
    this.score = 0;
    this.hiscoresButton;
    this.hiscoresLabel;
    this.exitButton;
    this.exitLabel;
}

Paloquiz.states.Finish.prototype = {

    create: function() {
        getJSON('/status', function(gameStatus) {
            if (gameStatus['status'] !== 'finish') {
                this.status.start('Router');
                return;
            }
            getJSON('/summary', function(summary) {
                this.score = summary['score'];
                this.answers = summary['answers'];
                this.numQuestions = summary['num_questions'];
                this.createScoreText();
                this.createUI();
                fbLogIn(function() {
                    fbGetUserScore(function(currentScore) {
                        if (this.score > currentScore) {
                            fbPublishScore(currentScore);
                        }
                    }, this);
                }, this);
            }, this);
        }, this);
    },

    shutdown: function() {},

    createScoreText: function() {;
        var scoreBaseTextSize = Math.round(.06 * this.world.height);
        var scoreTextColor = '#ff4f1a';

        var scoreHeader = this.add.text(
            this.world.centerX,
            this.world.height * .2,
            'Puntuación', {
                font: 'Pixel Art',
                fontSize: scoreBaseTextSize + 'px',
                fill: scoreTextColor,
                align: 'center'
            });
        scoreHeader.anchor.setTo(.5, 0);

        var scoreText = this.add.text(
            this.world.centerX,
            this.world.height * .33,
            this.score, {
                font: 'Pixel Art',
                fontSize: Math.round(scoreBaseTextSize * 2.2) + 'px',
                fill: scoreTextColor,
                align: 'center'
            });
        scoreText.anchor.setTo(.5, 0);
    },

    createUI: function() {
        // Hiscores
        this.hiscoresButton =
            this.add.button(this.world.centerX, this.world.height * .55,
                'finishButton',
                 function() {
                    fbLogIn(function() {
                        this.state.start('Highscores');
                    }, this);
                }, this, 2, 1, 0);
        this.hiscoresButton.smoothed = false;
        this.hiscoresButton.width = this.world.width * .6;
        this.hiscoresButton.height = this.hiscoresButton.width * .3;
        this.hiscoresButton.anchor.setTo(.5, 0);

        // Exit
        this.exitButton =
            this.add.button(this.hiscoresButton.x,
                this.hiscoresButton.y + this.hiscoresButton.height * 1.1,
                'finishButton',
                function() {
                    getJSON('/finish', function() {
                        this.state.start('Router');
                    }, this);
                }, this, 2, 1, 0);
        this.exitButton.smoothed = false;
        this.exitButton.width = this.hiscoresButton.width;
        this.exitButton.height = this.hiscoresButton.height;
        this.exitButton.anchor.setTo(.5, 0);

        // Labels
        var labelTextSize = Math.round(this.hiscoresButton.height / 3.0);
        var labelStyle = this.positionTextStyle = {
            font: 'Pixel Art',
            fontSize: labelTextSize + 'px',
            fill: 'white',
            align: 'center'
        };

        this.hiscoresLabel = this.add.text(
            this.world.centerX,
            this.hiscoresButton.y + this.hiscoresButton.height / 2,
            'Clasificación', labelStyle);
        this.hiscoresLabel.anchor.setTo(.5, .5);

        this.exitLabel = this.add.text(
            this.world.centerX,
            this.exitButton.y + this.exitButton.height / 2,
            'Salir', labelStyle);
        this.exitLabel.anchor.setTo(.5, .5);
    }
}<|endoftext|>


<commit_before>/*
 * Display a notification.
 */
function notify(msg, type) {

    const opts = {
        title: type ? type.charAt(0).toUpperCase() + type.slice(1) : 'Info',
        text: msg,
        type: type || 'info',
        hide : true,
        width: '400px',
        delay: 1500,
        buttons: {
            closer: true,
            sticker: false
        }
    };

    switch (type) {
        case "error":
        case "danger":
            opts.icon = 'fa fa-exclamation-circle';
            opts.hide = false;
            break;
        case "warning":
            opts.icon = 'fa fa-exclamation-circle';
            break;
        case "success":
            opts.icon = 'fa fa-thumbs-up';
            break;
        default:
            opts.icon = 'fa fa-info-circle';
            break;
    }

    return new PNotify(opts);
}<commit_msg>Longer flash message delay for non-success messages<commit_after>/*
 * Display a notification.
 */
function notify(msg, type) {

    const opts = {
        title: type ? type.charAt(0).toUpperCase() + type.slice(1) : 'Info',
        text: msg,
        type: type || 'info',
        hide : true,
        width: '400px',
        delay: 2500,
        buttons: {
            closer: true,
            sticker: false
        }
    };

    switch (type) {
        case "error":
        case "danger":
            opts.icon = 'fa fa-exclamation-circle';
            opts.hide = false;
            break;
        case "warning":
            opts.icon = 'fa fa-exclamation-circle';
            break;
        case "success":
            opts.icon = 'fa fa-thumbs-up';
            opts.delay = 1500;
            break;
        default:
            opts.icon = 'fa fa-info-circle';
            break;
    }

    return new PNotify(opts);
}<|endoftext|>


<commit_before>function applyQuirks(text) {

	// Case
	switch (user.case) {
		case "lower":
			text = text.toLowerCase();
			break;
		case "upper":
			text = text.toUpperCase();
			break;
		case "title":
			text = text.toLowerCase().replace(/\b\w/g, function(t) { return t.toUpperCase(); });
			break;
		case "inverted":
	        var buffer = text.replace(/[a-zA-Z]/g, function(t) {
		        var out = t.toUpperCase();
		        if (out==t) {
			        return t.toLowerCase();
		        } else {
			        return out;
		        }
	        }).replace(/\bI\b/g, 'i').replace(/,\s*[A-Z]/g, function(t) { return t.toLowerCase(); });
	        text = buffer.charAt(0).toLowerCase()+buffer.substr(1);
			break;
		case "alternating":
	        var buffer = text.toLowerCase().split('');
	        for(var i=0; i<buffer.length; i+=2){
		        buffer[i] = buffer[i].toUpperCase();
	        }
	        text = buffer.join('');
			break;
	}

	// Replacements
	for (i=0; i<user.replacements.length; i++) {
		var replacement = user.replacements[i];
		// Chrome doesn't do global string replacing so we need to loop this.
		while (text.indexOf(replacement[0])!=-1) {
			text = text.replace(replacement[0], replacement[1], 'g');
		}
	}

	// Prefix
	if (user.quirk_prefix!='') {
		text = user.quirk_prefix+' '+text;
	}

	return text

}

function depunct(txt) {
	return txt.replace(/[.,?!']/g, '');
}

<commit_msg>Don't do character replacement recursively. #38<commit_after>function applyQuirks(text) {

	// Case
	switch (user.case) {
		case "lower":
			text = text.toLowerCase();
			break;
		case "upper":
			text = text.toUpperCase();
			break;
		case "title":
			text = text.toLowerCase().replace(/\b\w/g, function(t) { return t.toUpperCase(); });
			break;
		case "inverted":
	        var buffer = text.replace(/[a-zA-Z]/g, function(t) {
		        var out = t.toUpperCase();
		        if (out==t) {
			        return t.toLowerCase();
		        } else {
			        return out;
		        }
	        }).replace(/\bI\b/g, 'i').replace(/,\s*[A-Z]/g, function(t) { return t.toLowerCase(); });
	        text = buffer.charAt(0).toLowerCase()+buffer.substr(1);
			break;
		case "alternating":
	        var buffer = text.toLowerCase().split('');
	        for(var i=0; i<buffer.length; i+=2){
		        buffer[i] = buffer[i].toUpperCase();
	        }
	        text = buffer.join('');
			break;
	}

	// Replacements
	for (i=0; i<user.replacements.length; i++) {
		var replacement = user.replacements[i];
		// We're doing it like this because regular expressions pick up slashes
		// and stuff as control characters, and string replacement only picks up
		// the first occurence in Chrome.
		text = text.split(replacement[0]).join(replacement[1])
	}

	// Prefix
	if (user.quirk_prefix!='') {
		text = user.quirk_prefix+' '+text;
	}

	return text

}

function depunct(txt) {
	return txt.replace(/[.,?!']/g, '');
}

<|endoftext|>


<commit_before>var summaryTemplate = null;
var singleResultTemplate = null;
var multiResultTemplate = null;
var resultStatsTemplate = null;

$(function() {
    summaryTemplate = _.template($("#summary-template").html());
    singleResultTemplate = _.template($("#single-result-template").html());
    multiResultTemplate = _.template($("#multi-result-template").html());
    resultStatsTemplate = _.template($("#result-stats-sidebar").html());
    $('.example-search').click(function() {
        $('#search-input').val($(this).text());
        $('#search-submit').click();
        return false;
    });
    buildDateUI();
});

var appendResult = function(obj) {
    var statusClass;
    switch (obj.docs[0].matter.status){
        case 'Adopted':
        case 'Approved':
        case 'Passed':
        case 'Recommended for Passage':
            statusClass = 'success';
            break;

        case 'Deferred and Published':
        case 'Direct Introduction':
        case 'In Committee':
        case 'Introduced':
        case 'Placed on File':
        case 'Recommended for Re-referral':
        case 'Re-referred':
            statusClass = 'warning';
            break;

        case 'Failed to Pass':
        case 'Recommended Do Not Pass':
        case 'Tabled':
        case 'Vetoed':
        case 'Void':
        case 'Withdrawn':
            statusClass = 'alert';
            break;
    }
    if(obj.docs.length > 1) {
        $(multiResultTemplate({docs: obj.docs, statusClass: statusClass})).appendTo('#search-results');
    } else {
        $(singleResultTemplate({doc: obj.docs[0], statusClass: statusClass})).appendTo('#search-results');
    }
};

//Need to store the results of the AJAX call somewhere so we can sort without hitting the database
var resultData = {
  'documents': [],
  'dateGroups': {},
  'rankGroups': {},
  'sidebarData': {}, //statsData
  'query': '',
  'dateRangeType': '',
  'ignoreRoutine': true,
  'queryQualifier': '',
  'isRanked': false
}

function populateResults(sortType){
    typeKeys = Object.keys(resultData[sortType]);
    if (sortType == "dateGroups"){
      typeKeys.sort(function(a,b) { return (b < a) ? -1 : 1 });
    } //Do we also need to sort ranks?
    for (i=0; i<typeKeys.length; i++) {
        var resultGroups = resultData[sortType][typeKeys[i]];
        $.each(resultGroups, function(j, g) {
            appendResult(g);
        });
    }
}

var doSearch = function(searchUrl, subscribeUrl) {
    showLoadingState();
    resultData.query = $('#search-input').val();
    resultData.dateRangeType = $('#date-range-type').val();
    resultData.ignoreRoutine = $('#ignore-routine').is(':checked');
    // only rank if not the default query
    if (subscribeUrl != null) {
      resultData.isRanked = true;
    }

    //build up summary stats for sidebar
    function buildResultStats(doc, statsData){
        var statTypes = {
            'Statuses': 'status',
            'Matter Types': 'type'
        };
        $.each(statTypes, function(key, matter_field) {
            if (!(key in statsData)){
                statsData[key] = {};
            }
            if(doc.matter[matter_field] in statsData[key]) {
                statsData[key][doc.matter[matter_field]] += 1;
            } else {
                statsData[key][doc.matter[matter_field]] = 1;
            }
        });
        return statsData;
    };

    function appendSummaryAndStats(total, qualifier, statsData){
        $('#results-summary').html($(summaryTemplate({
            total: total,
            query: $('#search-input').val(),
            qualifier: qualifier
        })));
        if (statsData) {
            $('#results-stats').html(resultStatsTemplate({statsData: statsData}));
        }
    }

    // group documents with their related matters
    function buildDateResults(data){
        if(data.documents.length > 0) {
            var dates = [];
            //TODO: create statsData global so we don't have to rebuild sidebar on sort-by-rank?
            $.each(data.documents, function(i, doc) {
                // dt is datetime
                var dt = doc.sort_date;
                // if the datetime hasn't already been added to the dates array
                if(dates.indexOf(dt) < 0) {
                    dates.push(dt);
                    resultData.dateGroups[dt] = {};
                }
                // if the datetime already exists in `groups`, it means the matter has multiple documents: add docs to existing matter
                // otherwise just create the `docs` property
                if(doc.matter.id in resultData.dateGroups[dt]) {
                    resultData.dateGroups[dt][doc.matter.id]['docs'].push(doc);
                } else {
                    resultData.dateGroups[dt][doc.matter.id] = {
                        'docs': [doc]
                    };
                }
                buildResultStats(doc, resultData.sidebarData);
            });

            appendSummaryAndStats(resultData.documents.length, resultData.queryQualifier, resultData.sidebarData);

            populateResults("dateGroups");
        }
    }

    function buildRankResults(data){
        if(data.documents.length > 0) {
            var ranks = [];
            $.each(data.documents, function(i, doc) {
                var rank = doc.rank;
                if(ranks.indexOf(rank) < 0) {
                    ranks.push(rank);
                    resultData.rankGroups[rank] = {};
                }
                if(doc.matter.id in resultData.rankGroups[rank]) {
                    resultData.rankGroups[rank][doc.matter.id]['docs'].push(doc);
                } else {
                    resultData.rankGroups[rank][doc.matter.id] = {
                        'docs': [doc]
                    };
                }
                buildResultStats(doc, resultData.sidebarData);
            });
            appendSummaryAndStats(resultData.documents.length, resultData.queryQualifier, resultData.sidebarData);

        }
        populateResults("rankGroups");
    }

    function showEmailForm(){
      $('#search-subscribe-form, #sort-by').show();
          $('#search-subscribe-form').submit(function(event) {
              handle_subscribe(event, subscribeUrl);
              return false;
          });
    };

    //used to determine language to display and time period to return
    switch (resultData.dateRangeType) {
        case 'past-year':
            resultData.queryQualifier = ' in the past year';
            break;

        case 'past-month':
            resultData.queryQualifier = ' in the last 30 days';
            break;

        case 'any':
            break;
    }

    $.ajax({
        url: searchUrl,
        data: {
            query: resultData.query,
            date_range: resultData.dateRangeType,
            ignore_routine: resultData.ignoreRoutine,
            is_ranked: resultData.isRanked
        }
    })
    .success(function(data) {
        console.log(data);

        resultData.documents = data.documents;
        if (data.is_ranked) {
          //NOTE: are results in ranked order already? If so, we just need to create meta.
          buildRankResults(data);
        } else {
          buildDateResults(data);
        }

        // don't let people subscribe to the default query
        if (subscribeUrl != null) {
          showEmailForm(subscribeUrl);
        }
        $("#search-results").foundation('reveal', 'reflow');

    })
    .error(function(xhr, status, errMsg) {
        alert(errMsg);
    })
    .complete(function(xhr, status) {
        hideLoadingState();
    });
}; // doSearch

$('#sort-relevance').on('click', function(){
  console.log(this);
  if (resultData.documents.length > 0){
    $('#sort-relevance .option').addClass('active');
    $(this).siblings().children().removeClass('active');
    $('#search-results').empty();
    populateResults("rankGroups");
  }
});

$('#sort-chron').on('click', function(){
  console.log(this);
  if (resultData.documents.length > 0){
    $('#sort-chron .option').addClass('active');
    $(this).siblings().children().removeClass('active');
    $('#search-results').empty();
    populateResults("dateGroups");
  }
});

<commit_msg>move sidebar rendering out of data parsing functions<commit_after>var summaryTemplate = null;
var singleResultTemplate = null;
var multiResultTemplate = null;
var resultStatsTemplate = null;

$(function() {
    summaryTemplate = _.template($("#summary-template").html());
    singleResultTemplate = _.template($("#single-result-template").html());
    multiResultTemplate = _.template($("#multi-result-template").html());
    resultStatsTemplate = _.template($("#result-stats-sidebar").html());
    $('.example-search').click(function() {
        $('#search-input').val($(this).text());
        $('#search-submit').click();
        return false;
    });
    buildDateUI();
});

var appendResult = function(obj) {
    var statusClass;
    switch (obj.docs[0].matter.status){
        case 'Adopted':
        case 'Approved':
        case 'Passed':
        case 'Recommended for Passage':
            statusClass = 'success';
            break;

        case 'Deferred and Published':
        case 'Direct Introduction':
        case 'In Committee':
        case 'Introduced':
        case 'Placed on File':
        case 'Recommended for Re-referral':
        case 'Re-referred':
            statusClass = 'warning';
            break;

        case 'Failed to Pass':
        case 'Recommended Do Not Pass':
        case 'Tabled':
        case 'Vetoed':
        case 'Void':
        case 'Withdrawn':
            statusClass = 'alert';
            break;
    }
    if(obj.docs.length > 1) {
        $(multiResultTemplate({docs: obj.docs, statusClass: statusClass})).appendTo('#search-results');
    } else {
        $(singleResultTemplate({doc: obj.docs[0], statusClass: statusClass})).appendTo('#search-results');
    }
};

//Need to store the results of the AJAX call somewhere so we can sort without hitting the database
var resultData = {
  'documents': [],
  'dateGroups': {},
  'rankGroups': {},
  'sidebarData': {}, //statsData
  'query': '',
  'dateRangeType': '',
  'ignoreRoutine': true,
  'queryQualifier': '',
  'isRanked': false
}

function populateResults(sortType){
    typeKeys = Object.keys(resultData[sortType]);
    if (sortType == "dateGroups"){
      typeKeys.sort(function(a,b) { return (b < a) ? -1 : 1 });
    } //Do we also need to sort ranks?
    for (i=0; i<typeKeys.length; i++) {
        var resultGroups = resultData[sortType][typeKeys[i]];
        $.each(resultGroups, function(j, g) {
            appendResult(g);
        });
    }

    appendSummaryAndStats(resultData.documents.length, resultData.queryQualifier, resultData.sidebarData);
}

function appendSummaryAndStats(total, qualifier, statsData){
    $('#results-summary').html($(summaryTemplate({
        total: total,
        query: $('#search-input').val(),
        qualifier: qualifier
    })));
    if (statsData) {
        $('#results-stats').html(resultStatsTemplate({statsData: statsData}));
    }
}

var doSearch = function(searchUrl, subscribeUrl) {
    showLoadingState();
    resultData.query = $('#search-input').val();
    resultData.dateRangeType = $('#date-range-type').val();
    resultData.ignoreRoutine = $('#ignore-routine').is(':checked');
    // only rank if not the default query
    if (subscribeUrl != null) {
      resultData.isRanked = true;
    }

    //build up summary stats for sidebar
    function buildResultStats(doc, statsData){
        var statTypes = {
            'Statuses': 'status',
            'Matter Types': 'type'
        };
        $.each(statTypes, function(key, matter_field) {
            if (!(key in statsData)){
                statsData[key] = {};
            }
            if(doc.matter[matter_field] in statsData[key]) {
                statsData[key][doc.matter[matter_field]] += 1;
            } else {
                statsData[key][doc.matter[matter_field]] = 1;
            }
        });
        return statsData;
    };

    // group documents with their related matters
    function buildDateResults(data){
        if(data.documents.length > 0) {
            var dates = [];
            //TODO: create statsData global so we don't have to rebuild sidebar on sort-by-rank?
            $.each(data.documents, function(i, doc) {
                // dt is datetime
                var dt = doc.sort_date;
                // if the datetime hasn't already been added to the dates array
                if(dates.indexOf(dt) < 0) {
                    dates.push(dt);
                    resultData.dateGroups[dt] = {};
                }
                // if the datetime already exists in `groups`, it means the matter has multiple documents: add docs to existing matter
                // otherwise just create the `docs` property
                if(doc.matter.id in resultData.dateGroups[dt]) {
                    resultData.dateGroups[dt][doc.matter.id]['docs'].push(doc);
                } else {
                    resultData.dateGroups[dt][doc.matter.id] = {
                        'docs': [doc]
                    };
                }
                buildResultStats(doc, resultData.sidebarData);
            });
        }
        populateResults("dateGroups");
    }

    function buildRankResults(data){
        if(data.documents.length > 0) {
            var ranks = [];
            $.each(data.documents, function(i, doc) {
                var rank = doc.rank;
                if(ranks.indexOf(rank) < 0) {
                    ranks.push(rank);
                    resultData.rankGroups[rank] = {};
                }
                if(doc.matter.id in resultData.rankGroups[rank]) {
                    resultData.rankGroups[rank][doc.matter.id]['docs'].push(doc);
                } else {
                    resultData.rankGroups[rank][doc.matter.id] = {
                        'docs': [doc]
                    };
                }
                buildResultStats(doc, resultData.sidebarData);
            });
        }
        populateResults("rankGroups");
    }

    function showEmailForm(){
      $('#search-subscribe-form, #sort-by').show();
          $('#search-subscribe-form').submit(function(event) {
              handle_subscribe(event, subscribeUrl);
              return false;
          });
    };

    //used to determine language to display and time period to return
    switch (resultData.dateRangeType) {
        case 'past-year':
            resultData.queryQualifier = ' in the past year';
            break;

        case 'past-month':
            resultData.queryQualifier = ' in the last 30 days';
            break;

        case 'any':
            break;
    }

    $.ajax({
        url: searchUrl,
        data: {
            query: resultData.query,
            date_range: resultData.dateRangeType,
            ignore_routine: resultData.ignoreRoutine,
            is_ranked: resultData.isRanked
        }
    })
    .success(function(data) {
        console.log(data);

        resultData.documents = data.documents;
        if (data.is_ranked) {
          //NOTE: are results in ranked order already? If so, we just need to create meta.
          buildRankResults(data);
        } else {
          buildDateResults(data);
        }

        // don't let people subscribe to the default query
        if (subscribeUrl != null) {
          showEmailForm(subscribeUrl);
        }
        $("#search-results").foundation('reveal', 'reflow');

    })
    .error(function(xhr, status, errMsg) {
        alert(errMsg);
    })
    .complete(function(xhr, status) {
        hideLoadingState();
    });
}; // doSearch

$('#sort-relevance').on('click', function(){
  console.log(this);
  if (resultData.documents.length > 0){
    $('#sort-relevance .option').addClass('active');
    $(this).siblings().children().removeClass('active');
    $('#search-results').empty();
    populateResults("rankGroups");
  }
});

$('#sort-chron').on('click', function(){
  console.log(this);
  if (resultData.documents.length > 0){
    $('#sort-chron .option').addClass('active');
    $(this).siblings().children().removeClass('active');
    $('#search-results').empty();
    populateResults("dateGroups");
  }
});

<|endoftext|>