<commit_before>/**
 * Module that controls the addon node settings. Includes Knockout view-model
 * for syncing data, and HGrid-folderpicker for selecting a folder.
 */
'use strict';

var ko = require('knockout');
require('knockout.punches');
var $ = require('jquery');
var Raven = require('raven-js');

var ZeroClipboard = require('zeroclipboard');
ZeroClipboard.config('/static/vendor/bower_components/zeroclipboard/dist/ZeroClipboard.swf');
var $osf = require('js/osfHelpers');
var oop = require('js/oop');
var FolderPickerViewModel = require('js/folderPickerNodeConfig');

ko.punches.enableAll();

var AddonFolderPickerViewModel = oop.extend(FolderPickerViewModel, {
    constructor: function(addonName, url, selector, folderPicker, opts) {
        var self = this;
        self.super.constructor.call(self, addonName, url, selector, folderPicker);
        // whether the auth token is valid
        self.validCredentials = ko.observable(true);
        // Emails of contributors, can only be populated by activating the share dialog
        self.emails = ko.observableArray([]);
        self.loading = ko.observable(false);
        // Whether the contributor emails have been loaded from the server
        self.loadedEmails = ko.observable(false);
        // externalAccounts
        self.accounts = ko.observable([]);
        // List of contributor emails as a comma-separated values
        self.emailList = ko.pureComputed(function() {
            return self.emails().join([', ']);
        });
        self.selectedFolderType = ko.pureComputed(function() {
            var userHasAuth = self.userHasAuth();
            var selected = self.selected();
            return (userHasAuth && selected) ? selected.type : '';
        });
        self.messages.SUBMIT_SETTINGS_SUCCESS =  ko.pureComputed(function() {
            return 'Successfully linked "' + $osf.htmlEscape(self.folder().name) + '". Go to the Files page to view your content.';
        });
        // Overrides
        self.options = {
            onPickFolder: function(evt, item) {
                evt.preventDefault();
                var name = item.data.path !== '/' ? item.data.path : '/ (Full ' + self.addonName + ')';
                self.selected({
                    name: name,
                    path: item.data.path,
                    id: item.data.id
                });
                return false; // Prevent event propagation
            }
        };
        // Overrides
        self.options = $.extend(self.options, opts);
        // Treebeard config
        self.treebeardOptions = $.extend(
            {},
            FolderPickerViewModel.prototype.treebeardOptions,
            {
                onPickFolder: function(evt, item) {
                    return this.options.onPickFolder.call(this, evt, item);
                }.bind(this),
                resolveLazyloadUrl: function(item) {
                    return item.data.urls.folders;
                }
            }
        );
    },
    toggleShare: function() {
        if (this.currentDisplay() === this.SHARE) {
            this.currentDisplay(null);
        } else {
            // Clear selection
            this.cancelSelection();
            this.currentDisplay(this.SHARE);
            this.activateShare();
        }
    },
    fetchEmailList: function(){
        var self = this;

        var ret = $.Deferred();
        var promise = ret.promise();
        if(!self.loadedEmails()){
            $.ajax({
                url: self.urls().emails,
                type: 'GET',
                dataType: 'json'
            }).done(function(res){
                self.loadedEmails(true);
                ret.resolve(res.results.emails);
            }).fail(function(xhr, status, error){
                Raven.captureMessage('Could not GET ' + self.addonName + ' email list', {
                    url: self.urls().emails,
                    textStatus: status,
                    error: error
                });
                ret.reject(xhr, status, error);
            });
        }
        else{
            ret.resolve(self.emails());
        }
        return promise;
    },
    activateShare: function() {
        var self = this;
        self.fetchEmailList()
            .done(function(emails){
                self.emails(emails);
            });
        var $copyBtn = $(self.selector).find('.copyBtn');
        new ZeroClipboard($copyBtn);
    },
    _updateCustomFields: function(settings){
        this.validCredentials(settings.validCredentials);
        this.canShare(settings.canShare || false);
    },
    fetchAccounts: function() {
        var self = this;
        var request = $.get(self.urls().accounts);
        request.then(function(data) {
            return data.accounts;
        });
        request.fail(function(xhr, textStatus, error) {
            self.changeMessage(self.messages.UPDATE_ACCOUNTS_ERROR(), 'text-warning');
            Raven.captureMessage('Could not GET ' + self.addonName + ' accounts for user', {
                url: self.url,
                textStatus: textStatus,
                error: error
            });
        });
        return request;
    },
    updateAccounts: function() {
        var self = this;
        return self.fetchAccounts()
        .done(function(accounts) {
            self.accounts(accounts.map(function(account) {
                return {
                    name: account.display_name,
                    id: account.id
                };
            }));
        });
    },
    /**
     * Allows a user to create an access token from the nodeSettings page
     */
    connectAccount: function() {
        window.location.href = this.urls().auth;
    }
});

// Public API
function AddonNodeConfig(addonName, selector, url, folderPicker, opts) {
    var self = this;
    self.url = url;
    self.folderPicker = folderPicker;
    if (typeof opts === 'undefined') {
        opts = {};
    }
    self.viewModel = new AddonFolderPickerViewModel(addonName, url, selector, folderPicker, opts);
    self.viewModel.updateFromData();
    $osf.applyBindings(self.viewModel, selector);
}

module.exports = {
    AddonNodeConfig: AddonNodeConfig,
    _AddonNodeConfigViewModel: AddonFolderPickerViewModel
};
<commit_msg>Style changes in response to PR comments<commit_after>/**
 * Module that controls the addon node settings. Includes Knockout view-model
 * for syncing data, and HGrid-folderpicker for selecting a folder.
 */
'use strict';

var ko = require('knockout');
require('knockout.punches');
var $ = require('jquery');
var Raven = require('raven-js');

var ZeroClipboard = require('zeroclipboard');
ZeroClipboard.config('/static/vendor/bower_components/zeroclipboard/dist/ZeroClipboard.swf');
var $osf = require('js/osfHelpers');
var oop = require('js/oop');
var FolderPickerViewModel = require('js/folderPickerNodeConfig');

ko.punches.enableAll();

var AddonFolderPickerViewModel = oop.extend(FolderPickerViewModel, {
    constructor: function(addonName, url, selector, folderPicker, opts) {
        var self = this;
        self.super.constructor.call(self, addonName, url, selector, folderPicker);
        // whether the auth token is valid
        self.validCredentials = ko.observable(true);
        // Emails of contributors, can only be populated by activating the share dialog
        self.emails = ko.observableArray([]);
        self.loading = ko.observable(false);
        // Whether the contributor emails have been loaded from the server
        self.loadedEmails = ko.observable(false);
        // externalAccounts
        self.accounts = ko.observable([]);
        // List of contributor emails as a comma-separated values
        self.emailList = ko.pureComputed(function() {
            return self.emails().join([', ']);
        });
        self.selectedFolderType = ko.pureComputed(function() {
            var userHasAuth = self.userHasAuth();
            var selected = self.selected();
            return (userHasAuth && selected) ? selected.type : '';
        });
        self.messages.SUBMIT_SETTINGS_SUCCESS =  ko.pureComputed(function() {
            return 'Successfully linked "' + $osf.htmlEscape(self.folder().name) + '". Go to the Files page to view your content.';
        });
        // Overrides
        var defaults = {
            onPickFolder: function(evt, item) {
                evt.preventDefault();
                var name = item.data.path !== '/' ? item.data.path : '/ (Full ' + self.addonName + ')';
                self.selected({
                    name: name,
                    path: item.data.path,
                    id: item.data.id
                });
                return false; // Prevent event propagation
            }
        };
        // Overrides
        self.options = $.extend({}, defaults, opts);
        // Treebeard config
        self.treebeardOptions = $.extend(
            {},
            FolderPickerViewModel.prototype.treebeardOptions,
            {
                onPickFolder: function(evt, item) {
                    return this.options.onPickFolder.call(this, evt, item);
                }.bind(this),
                resolveLazyloadUrl: function(item) {
                    return item.data.urls.folders;
                }
            }
        );
    },
    toggleShare: function() {
        if (this.currentDisplay() === this.SHARE) {
            this.currentDisplay(null);
        } else {
            // Clear selection
            this.cancelSelection();
            this.currentDisplay(this.SHARE);
            this.activateShare();
        }
    },
    fetchEmailList: function(){
        var self = this;

        var ret = $.Deferred();
        var promise = ret.promise();
        if(!self.loadedEmails()){
            $.ajax({
                url: self.urls().emails,
                type: 'GET',
                dataType: 'json'
            }).done(function(res){
                self.loadedEmails(true);
                ret.resolve(res.results.emails);
            }).fail(function(xhr, status, error){
                Raven.captureMessage('Could not GET ' + self.addonName + ' email list', {
                    url: self.urls().emails,
                    textStatus: status,
                    error: error
                });
                ret.reject(xhr, status, error);
            });
        }
        else{
            ret.resolve(self.emails());
        }
        return promise;
    },
    activateShare: function() {
        var self = this;
        self.fetchEmailList()
            .done(function(emails){
                self.emails(emails);
            });
        var $copyBtn = $(self.selector).find('.copyBtn');
        new ZeroClipboard($copyBtn);
    },
    _updateCustomFields: function(settings){
        this.validCredentials(settings.validCredentials);
        this.canShare(settings.canShare || false);
    },
    fetchAccounts: function() {
        var self = this;
        var request = $.get(self.urls().accounts);
        request.then(function(data) {
            return data.accounts;
        });
        request.fail(function(xhr, textStatus, error) {
            self.changeMessage(self.messages.UPDATE_ACCOUNTS_ERROR(), 'text-warning');
            Raven.captureMessage('Could not GET ' + self.addonName + ' accounts for user', {
                url: self.url,
                textStatus: textStatus,
                error: error
            });
        });
        return request;
    },
    updateAccounts: function() {
        var self = this;
        return self.fetchAccounts()
        .done(function(accounts) {
            self.accounts(accounts.map(function(account) {
                return {
                    name: account.display_name,
                    id: account.id
                };
            }));
        });
    },
    /**
     * Allows a user to create an access token from the nodeSettings page
     */
    connectAccount: function() {
        window.location.href = this.urls().auth;
    }
});

// Public API
function AddonNodeConfig(addonName, selector, url, folderPicker, opts) {
    var self = this;
    self.url = url;
    self.folderPicker = folderPicker;
    if (typeof opts === 'undefined') {
        opts = {};
    }
    self.viewModel = new AddonFolderPickerViewModel(addonName, url, selector, folderPicker, opts);
    self.viewModel.updateFromData();
    $osf.applyBindings(self.viewModel, selector);
}

module.exports = {
    AddonNodeConfig: AddonNodeConfig,
    _AddonNodeConfigViewModel: AddonFolderPickerViewModel
};
<|endoftext|>


<commit_before>/* ---- particles.js config ---- */

particlesJS("particles-js", {
  "particles": {
    "number": {
      "value": 100,
      "density": {
        "enable": true,
        "value_area":1000
      }
    },
    "color": {
      "value": ["#aa73ff", "#f8c210", "#83d238", "#33b1f8"]
    },

    "shape": {
      "type": "circle",
      "stroke": {
        "width": 0,
        "color": "#fff"
      },
      "polygon": {
        "nb_sides": 5
      },
      "image": {
        "src": "img/github.svg",
        "width": 100,
        "height": 100
      }
    },
    "opacity": {
      "value": 0.6,
      "random": false,
      "anim": {
        "enable": false,
        "speed": 1,
        "opacity_min": 0.1,
        "sync": false
      }
    },
    "size": {
      "value": 2,
      "random": true,
      "anim": {
        "enable": false,
        "speed": 40,
        "size_min": 0.1,
        "sync": false
      }
    },
    "line_linked": {
      "enable": true,
      "distance": 120,
      "color": "#ffffff",
      "opacity": 0.4,
      "width": 1
    },
  },
  "interactivity": {
    "detect_on": "canvas",
    "events": {
      "onhover": {
        "enable": true,
        "mode": "grab"
      },
      "onclick": {
        "enable": false
      },
      "resize": true
    },
    "modes": {
      "grab": {
        "distance": 140,
        "line_linked": {
          "opacity": 1
        }
      },
      "bubble": {
        "distance": 400,
        "size": 40,
        "duration": 2,
        "opacity": 8,
        "speed": 3
      },
      "repulse": {
        "distance": 200,
        "duration": 0.4
      },
      "push": {
        "particles_nb": 4
      },
      "remove": {
        "particles_nb": 2
      }
    }
  },
  "retina_detect": true
});
<commit_msg>Update index.js<commit_after>/* ---- particles.js config ---- */
particlesJS("particles-js", {
  "particles": {
    "number": {
      "value": 100,
      "density": {
        "enable": true,
        "value_area":1000
      }
    },
    "color": {
      "value": ["#aa73ff", "#f8c210", "#83d238", "#33b1f8"]
    },

    "shape": {
      "type": "circle",
      "stroke": {
        "width": 0,
        "color": "#fff"
      },
      "polygon": {
        "nb_sides": 5
      },
      "image": {
        "src": "img/github.svg",
        "width": 100,
        "height": 100
      }
    },
    "opacity": {
      "value": 0.6,
      "random": false,
      "anim": {
        "enable": false,
        "speed": 1,
        "opacity_min": 0.1,
        "sync": false
      }
    },
    "size": {
      "value": 2,
      "random": true,
      "anim": {
        "enable": false,
        "speed": 40,
        "size_min": 0.1,
        "sync": false
      }
    },
    "line_linked": {
      "enable": true,
      "distance": 120,
      "color": "#ffffff",
      "opacity": 0.4,
      "width": 1
    },
  },
  "interactivity": {
    "detect_on": "canvas",
    "events": {
      "onhover": {
        "enable": true,
        "mode": "grab"
      },
      "onclick": {
        "enable": false
      },
      "resize": true
    },
    "modes": {
      "grab": {
        "distance": 140,
        "line_linked": {
          "opacity": 1
        }
      },
      "bubble": {
        "distance": 400,
        "size": 40,
        "duration": 2,
        "opacity": 8,
        "speed": 3
      },
      "repulse": {
        "distance": 200,
        "duration": 0.4
      },
      "push": {
        "particles_nb": 4
      },
      "remove": {
        "particles_nb": 2
      }
    }
  },
  "retina_detect": true
});

$(document).ready(function(){
	$(".scroll").click(function(event){
		event.preventDefault();
		$("html,body").animate({scrollTop:$(this.hash).offset().top}, 500);
		$('.navbar-default a').removeClass('selected');
		$(this).addClass('selected');
    	});
});
<|endoftext|>


<commit_before>import React, {Component} from 'react';

import DropdownSelect from 'src/DropdownSelect';
import Widget from 'src/Widget';
import WidgetHeader from 'src/WidgetHeader';
import Theme from 'src/Theme';

import PropTypeDescTable from 'components/PropTypeDescTable';
import doc from 'assets/propTypes/DropdownSelect.json';

import 'sass/containers/app/modules/fields/DropdownSelectExamples.scss';

class DropdownSelectExamples extends Component {

    constructor(props) {

        super(props);

        this.data = ['test0', 'test1', {
            text: 'test2',
            value: 2,
            desc: 'Here is test2.',
            onClick() {
                console.log('test2 selected!');
            }
        }, 'test3', 'test4', 'test5', {
            text: 'test6',
            value: 6,
            desc: 'Here is test6.',
            onClick() {
                console.log('test6 selected!');
            }
        }, 'test7', 'test8', 'test9'];

    }

    onChange = value => {
        console.log(value);
    };

    render() {

        const data = ['a000', 'booo', {
            text: 'c000',
            value: 2,
            desc: 'Here is test2.'
        }, 'dtest3', 'etest4', 'ftest5', {
            text: 'test6',
            value: 6
        }, 'hhhh', 'yywe', 'people'];

        return (

Dropdown Select

                    Dropdown Select
                    use to store operating elements. Click on the contact
                    and a drop-down menu will appear. You can select from the list and execute the appropriate command.

Examples

                <Widget>

                    <WidgetHeader className="example-header" title="Basic"/>

Dropdown Select simple example.

                                    <DropdownSelect data={this.data}
                                                    renderer={data => {
                                                        return data && typeof data === 'object' ?
                                                            `${data.text} (${data.value})`
                                                            :
                                                            `${data} (${data})`;
                                                    }}
                                                    onChange={this.onChange}/>

                </Widget>

                <Widget>

                    <WidgetHeader className="example-header" title="Self defined theme"/>

Set the autoClose property to false,the select list will not close when
                                    choose one item.

                                    <DropdownSelect theme={Theme.PRIMARY}
                                                    popupTheme={Theme.PRIMARY}
                                                    data={this.data}
                                                    onChange={this.onChange}/>

                </Widget>

                <Widget>

                    <WidgetHeader className="example-header" title="With Filter"/>

Set the filter property to true,the DropdownSelect will have user
                                        search input.

                                    <DropdownSelect data={this.data}
                                                    useFilter={true}
                                                    useSelectAll={true}
                                                    selectAllText="All Example Items"
                                                    selectMode={DropdownSelect.SelectMode.MULTI_SELECT}
                                                    autoClose={false}
                                                    onChange={this.onChange}/>

                </Widget>

                <Widget>

                    <WidgetHeader className="example-header" title="With multi"/>

                                        Set the mod property to
                                        DropdownSelect.SelectMode.MULTI_SELECT,
                                        the DropdownSelect can be Multiselect.

                                    <DropdownSelect data={this.data}
                                                    autoClose={false}
                                                    selectMode={DropdownSelect.SelectMode.MULTI_SELECT}
                                                    onChange={this.onChange}/>

                </Widget>

                <Widget>

                    <WidgetHeader className="example-header" title="Self defined theme"/>

Set the autoClose property to false,the select list will not close when
                                    choose one item.

                                    <DropdownSelect data={data}
                                                    isHiddenInputFilter={true}
                                                    onChange={this.onChange}/>

                </Widget>

Properties

                <PropTypeDescTable data={doc}/>

        );
    }
};

export default DropdownSelectExamples;<commit_msg>update DropdownSelectExamples<commit_after>import React, {Component} from 'react';

import DropdownSelect from 'src/DropdownSelect';
import Widget from 'src/Widget';
import WidgetHeader from 'src/WidgetHeader';
import Theme from 'src/Theme';

import PropTypeDescTable from 'components/PropTypeDescTable';
import doc from 'assets/propTypes/DropdownSelect.json';

import 'sass/containers/app/modules/fields/DropdownSelectExamples.scss';

class DropdownSelectExamples extends Component {

    constructor(props) {

        super(props);

        this.data = ['test0', 'test1', {
            text: 'test2',
            value: 2,
            desc: 'Here is test2.',
            onClick() {
                console.log('test2 selected!');
            }
        }, 'test3', 'test4', 'test5', {
            text: 'test6',
            value: 6,
            desc: 'Here is test6.',
            onClick() {
                console.log('test6 selected!');
            }
        }, 'test7', 'test8', 'test9'];

    }

    onChange = value => {
        console.log(value);
    };

    render() {

        const data = ['a000', 'booo', {
            text: 'c000',
            value: 2,
            desc: 'Here is test2.'
        }, 'dtest3', 'etest4', 'ftest5', {
            text: 'test6',
            value: 6
        }, 'hhhh', 'yywe', 'people'];

        return (

Dropdown Select

                    Dropdown Select
                    use to store operating elements. Click on the contact
                    and a drop-down menu will appear. You can select from the list and execute the appropriate command.

Examples

                <Widget>

                    <WidgetHeader className="example-header" title="Basic"/>

Dropdown Select simple example.

                                    <DropdownSelect data={this.data}
                                                    renderer={data => {
                                                        return data && typeof data === 'object' ?
                                                            `${data.text} (${data.value})`
                                                            :
                                                            `${data} (${data})`;
                                                    }}
                                                    onChange={this.onChange}/>

                </Widget>

                <Widget>

                    <WidgetHeader className="example-header" title="Self defined theme"/>

Set the autoClose property to false,the select list will not close when
                                    choose one item.

                                    <DropdownSelect theme={Theme.PRIMARY}
                                                    popupTheme={Theme.PRIMARY}
                                                    data={this.data}
                                                    onChange={this.onChange}/>

                </Widget>

                <Widget>

                    <WidgetHeader className="example-header" title="With Filter"/>

Set the filter property to true,the DropdownSelect will have user
                                        search input.

                                    <DropdownSelect data={this.data}
                                                    useFilter={true}
                                                    useSelectAll={true}
                                                    selectAllText="All Example Items"
                                                    selectMode={DropdownSelect.SelectMode.MULTI_SELECT}
                                                    autoClose={false}
                                                    onChange={this.onChange}/>

                </Widget>

                <Widget>

                    <WidgetHeader className="example-header" title="With multi"/>

                                        Set the mod property to
                                        DropdownSelect.SelectMode.MULTI_SELECT,
                                        the DropdownSelect can be Multiselect.

                                    <DropdownSelect data={this.data}
                                                    autoClose={false}
                                                    selectMode={DropdownSelect.SelectMode.MULTI_SELECT}
                                                    onChange={this.onChange}
                                                    checkboxUncheckedIconCls={'far fa-window-close'}
                                                    checkboxCheckedIconCls={'fas fa-window-close'}/>

                </Widget>

                <Widget>

                    <WidgetHeader className="example-header" title="Self defined theme"/>

Set the autoClose property to false,the select list will not close when
                                    choose one item.

                                    <DropdownSelect data={data}
                                                    isHiddenInputFilter={true}
                                                    onChange={this.onChange}/>

                </Widget>

Properties

                <PropTypeDescTable data={doc}/>

        );
    }
};

export default DropdownSelectExamples;<|endoftext|>


<commit_before>'use strict';

describe('text editor', function() {

    beforeEach(window.module('superdesk.apps.publish'));
    beforeEach(window.module('superdesk.apps.editor'));
    beforeEach(window.module('superdesk.apps.editor.spellcheck'));
    beforeEach(window.module('superdesk.templates-cache'));

    beforeEach(function() {
        // remove all elements from body
        document.body = document.createElement('body');
    });

    function createScope(text, $rootScope) {
        var scope = $rootScope.$new();
        scope.node = document.createElement('p');
        scope.node.innerHTML = text;
        scope.model = {
            $viewValue: text,
            $setViewValue: function(value) {
                this.$viewValue = value;
            }
        };
        scope.medium = {
            exportSelection: function() {
                return {start: 0, end: 0};
            },
            importSelection: function(pos) {
                return;
            }
        };
        document.body.appendChild(scope.node);
        spyOn(scope.model, '$setViewValue').and.callThrough();
        return scope;
    }

    it('can spellcheck', inject(function(editor, spellcheck, $q, $rootScope) {
        editor.setSettings({spellcheck: true});
        spyOn(spellcheck, 'errors').and.returnValue($q.when([{word: 'test', index: 0}]));
        var scope = createScope('test', $rootScope);
        editor.registerScope(scope);
        editor.renderScope(scope);
        $rootScope.$digest();

        expect(scope.node.innerHTML).toBe('test');
    }));

    it('can highlight a sentence word for capitalization', inject(function(editor, spellcheck, $q, $rootScope) {
        editor.setSettings({spellcheck: true});
        spyOn(spellcheck, 'errors').and.returnValue($q.when([{word: 'test', index: 0, sentenceWord: true}]));
        var scope = createScope('test', $rootScope);
        editor.registerScope(scope);
        editor.renderScope(scope);
        $rootScope.$digest();

        expect(scope.node.innerHTML).toBe('test');
    }));

    it('can remove highlights but keep marker', inject(function(editor, $q, $rootScope) {
        editor.setSettings({spellcheck: true});
        var content = 'test foo error it';
        var scope = createScope(content, $rootScope);
        var html = editor.cleanScope(scope);
        expect(html).toBe('test foo error it');
    }));

    it('can findreplace', inject(function(editor, spellcheck, $q, $rootScope, $timeout) {
        editor.setSettings({spellcheck: true});
        spyOn(spellcheck, 'errors').and.returnValue($q.when([{word: 'test', index: 0}]));
        var scope = createScope('test $foo and $foo', $rootScope);
        editor.registerScope(scope);

        var diff = {};
        diff.bar = '';
        diff.$foo = '';
        editor.setSettings({findreplace: {diff: diff}});
        editor.render();
        $timeout.flush();

        $rootScope.$digest();
        var foo = '$foo';
        var fooActive = '$foo';
        expect(scope.node.innerHTML).toBe('test ' + fooActive + ' and ' + foo);

        editor.selectNext();
        expect(scope.node.innerHTML).toBe('test ' + foo + ' and ' + fooActive);

        editor.selectNext();
        expect(scope.node.innerHTML).toBe('test ' + fooActive + ' and ' + foo);

        editor.selectPrev();
        expect(scope.node.innerHTML).toBe('test ' + foo + ' and ' + fooActive);

        editor.selectPrev();
        expect(scope.node.innerHTML).toBe('test ' + fooActive + ' and ' + foo);

        editor.replace('test');
        expect(scope.node.innerHTML).toBe('test test and ' + foo);

        diff = {};
        diff.test = '';
        editor.setSettings({findreplace: {diff: diff}});
        editor.render();
        $timeout.flush();
        editor.replaceAll('bar');
        expect(scope.node.innerHTML).toBe('bar bar and $foo');

        editor.setSettings({findreplace: null});
        editor.render();
        $rootScope.$digest();
        expect(scope.node.innerHTML).toContain('sderror');
        expect(scope.node.innerHTML).not.toContain('active');
    }));

    it('can replace abbreviations', inject(function(editor, spellcheck, $q, $rootScope, $timeout) {
        editor.setSettings({spellcheck: true});
        var abbreviations = {
            IMF: 'International Monetory Fund',
            WHO: 'World Health Organisation',
            UN: 'United Nations'
        };
        spyOn(spellcheck, 'getAbbreviationsDict').and.returnValue($q.when(abbreviations));
        var scope = createScope('test', $rootScope);
        editor.registerScope(scope);
        scope.node.classList.add('typing');
        scope.node.innerHTML = 'foo';
        editor.commit();
        expect(scope.model.$setViewValue).not.toHaveBeenCalled();
        $rootScope.$digest();
        expect(scope.model.$setViewValue).toHaveBeenCalledWith('foo');
        expect(scope.node.innerHTML).toBe('foo');

        scope.node.innerHTML = 'foo IMF*';
        editor.commit();
        $rootScope.$digest();
        expect(scope.node.innerHTML).toBe('foo ' + abbreviations.IMF);

        scope.node.innerHTML = 'foo IMF IMF* IMF*';
        editor.commit();
        $rootScope.$digest();
        expect(scope.node.innerHTML).toBe('foo IMF ' + abbreviations.IMF + ' ' + abbreviations.IMF);

        scope.node.innerHTML = 'foo IMF* WHO*';
        editor.commit();
        $rootScope.$digest();
        expect(scope.node.innerHTML).toBe('foo ' + abbreviations.IMF + ' ' + abbreviations.WHO);

    }));

    it('can save model value', inject(function(editor, $rootScope, spellcheck, $q) {
        spyOn(spellcheck, 'getAbbreviationsDict').and.returnValue($q.when({}));
        var scope = createScope('foo', $rootScope);
        editor.registerScope(scope);

        scope.node.innerHTML = 'foo';
        editor.commit();
        expect(scope.model.$setViewValue).not.toHaveBeenCalled();

        editor.undo(scope);
        expect(scope.model.$setViewValue).not.toHaveBeenCalled();
        editor.redo(scope);
        expect(scope.model.$setViewValue).not.toHaveBeenCalled();

        scope.node.innerHTML = 'bar';
        editor.commit();
        $rootScope.$digest();
        expect(scope.model.$setViewValue).toHaveBeenCalledWith('bar');

        scope.node.innerHTML = 'baz';
        editor.commit();
        $rootScope.$digest();
        editor.undo(scope);
        editor.undo(scope);
        expect(scope.node.innerHTML).toBe('foo');

        editor.redo(scope);
        expect(scope.node.innerHTML).toBe('bar');

        editor.redo(scope);
        expect(scope.node.innerHTML).toBe('baz');

        editor.undo(scope);
        editor.undo(scope);

        scope.node.innerHTML = 'test';
        editor.commit();
        $rootScope.$digest();
        editor.redo(scope);
        expect(scope.node.innerHTML).toBe('test');
    }));

    it('can check if keyboard event is important or not', inject(function(editor) {
        expect(editor.shouldIgnore({keyCode: 16})).toBe(true);
        expect(editor.shouldIgnore({keyCode: 39})).toBe(true);
        expect(editor.shouldIgnore({shiftKey: true, ctrlKey: true, keyCode: 65})).toBe(true);
        expect(editor.shouldIgnore({keyCode: 65})).toBe(false);
    }));

    it('can observe and apply disableEditorToolbar configuration option',
    inject(function($rootScope, $compile, config) {
        var scope = $rootScope.$new();

        config.editor = {
            disableEditorToolbar: true
        };

        scope.item = {
            body_html: '
test
',
            type: 'text'
        };

        scope.node = document.createElement('p');
        scope.node.innerHTML = 'test';

        window.MediumEditor = jasmine.createSpy('editor');

        var $elm = $compile('

')(scope);
        scope.$digest();

        var isoScope = $elm.isolateScope();

        isoScope.$digest();

        expect(window.MediumEditor).toHaveBeenCalled();
        expect(window.MediumEditor.calls.argsFor(0)[1].disableToolbar).
            toBe(config.editor.disableEditorToolbar);
    }));

    it('can observe and set tabindex from data-config if defined',
    inject(function($rootScope, $compile, config) {
        var scope = $rootScope.$new();

        scope.item = {
            body_html: '
test
',
            type: 'text'
        };

        scope.node = document.createElement('p');
        scope.node.innerHTML = 'test';

        window.MediumEditor = jasmine.createSpy('editor');

        var $elm = $compile('

')(scope);
        scope.$digest();

        var isoScope = $elm.isolateScope();

        isoScope.$digest();

        expect(isoScope.config.tabindex).toBe(1);
        expect(window.MediumEditor).toHaveBeenCalled();
    }));

});
<commit_msg>fix(editor): fix editor test<commit_after>'use strict';

describe('text editor', function() {

    beforeEach(window.module('superdesk.apps.publish'));
    beforeEach(window.module('superdesk.apps.editor'));
    beforeEach(window.module('superdesk.apps.editor.spellcheck'));
    beforeEach(window.module('superdesk.templates-cache'));

    beforeEach(function() {
        // remove all elements from body
        document.body = document.createElement('body');

        window.MediumEditor = jasmine.createSpy('editor').and.returnValue({
            subscribe: jasmine.createSpy('subscribe')
        });
    });

    function createScope(text, $rootScope) {
        var scope = $rootScope.$new();
        scope.node = document.createElement('p');
        scope.node.innerHTML = text;
        scope.model = {
            $viewValue: text,
            $setViewValue: function(value) {
                this.$viewValue = value;
            }
        };
        scope.medium = {
            exportSelection: function() {
                return {start: 0, end: 0};
            },
            importSelection: function(pos) {
                return;
            }
        };
        document.body.appendChild(scope.node);
        spyOn(scope.model, '$setViewValue').and.callThrough();
        return scope;
    }

    it('can spellcheck', inject(function(editor, spellcheck, $q, $rootScope) {
        editor.setSettings({spellcheck: true});
        spyOn(spellcheck, 'errors').and.returnValue($q.when([{word: 'test', index: 0}]));
        var scope = createScope('test', $rootScope);
        editor.registerScope(scope);
        editor.renderScope(scope);
        $rootScope.$digest();

        expect(scope.node.innerHTML).toBe('test');
    }));

    it('can highlight a sentence word for capitalization', inject(function(editor, spellcheck, $q, $rootScope) {
        editor.setSettings({spellcheck: true});
        spyOn(spellcheck, 'errors').and.returnValue($q.when([{word: 'test', index: 0, sentenceWord: true}]));
        var scope = createScope('test', $rootScope);
        editor.registerScope(scope);
        editor.renderScope(scope);
        $rootScope.$digest();

        expect(scope.node.innerHTML).toBe('test');
    }));

    it('can remove highlights but keep marker', inject(function(editor, $q, $rootScope) {
        editor.setSettings({spellcheck: true});
        var content = 'test foo error it';
        var scope = createScope(content, $rootScope);
        var html = editor.cleanScope(scope);
        expect(html).toBe('test foo error it');
    }));

    it('can findreplace', inject(function(editor, spellcheck, $q, $rootScope, $timeout) {
        editor.setSettings({spellcheck: true});
        spyOn(spellcheck, 'errors').and.returnValue($q.when([{word: 'test', index: 0}]));
        var scope = createScope('test $foo and $foo', $rootScope);
        editor.registerScope(scope);

        var diff = {};
        diff.bar = '';
        diff.$foo = '';
        editor.setSettings({findreplace: {diff: diff}});
        editor.render();
        $timeout.flush();

        $rootScope.$digest();
        var foo = '$foo';
        var fooActive = '$foo';
        expect(scope.node.innerHTML).toBe('test ' + fooActive + ' and ' + foo);

        editor.selectNext();
        expect(scope.node.innerHTML).toBe('test ' + foo + ' and ' + fooActive);

        editor.selectNext();
        expect(scope.node.innerHTML).toBe('test ' + fooActive + ' and ' + foo);

        editor.selectPrev();
        expect(scope.node.innerHTML).toBe('test ' + foo + ' and ' + fooActive);

        editor.selectPrev();
        expect(scope.node.innerHTML).toBe('test ' + fooActive + ' and ' + foo);

        editor.replace('test');
        expect(scope.node.innerHTML).toBe('test test and ' + foo);

        diff = {};
        diff.test = '';
        editor.setSettings({findreplace: {diff: diff}});
        editor.render();
        $timeout.flush();
        editor.replaceAll('bar');
        expect(scope.node.innerHTML).toBe('bar bar and $foo');

        editor.setSettings({findreplace: null});
        editor.render();
        $rootScope.$digest();
        expect(scope.node.innerHTML).toContain('sderror');
        expect(scope.node.innerHTML).not.toContain('active');
    }));

    it('can replace abbreviations', inject(function(editor, spellcheck, $q, $rootScope, $timeout) {
        editor.setSettings({spellcheck: true});
        var abbreviations = {
            IMF: 'International Monetory Fund',
            WHO: 'World Health Organisation',
            UN: 'United Nations'
        };
        spyOn(spellcheck, 'getAbbreviationsDict').and.returnValue($q.when(abbreviations));
        var scope = createScope('test', $rootScope);
        editor.registerScope(scope);
        scope.node.classList.add('typing');
        scope.node.innerHTML = 'foo';
        editor.commit();
        expect(scope.model.$setViewValue).not.toHaveBeenCalled();
        $rootScope.$digest();
        expect(scope.model.$setViewValue).toHaveBeenCalledWith('foo');
        expect(scope.node.innerHTML).toBe('foo');

        scope.node.innerHTML = 'foo IMF*';
        editor.commit();
        $rootScope.$digest();
        expect(scope.node.innerHTML).toBe('foo ' + abbreviations.IMF);

        scope.node.innerHTML = 'foo IMF IMF* IMF*';
        editor.commit();
        $rootScope.$digest();
        expect(scope.node.innerHTML).toBe('foo IMF ' + abbreviations.IMF + ' ' + abbreviations.IMF);

        scope.node.innerHTML = 'foo IMF* WHO*';
        editor.commit();
        $rootScope.$digest();
        expect(scope.node.innerHTML).toBe('foo ' + abbreviations.IMF + ' ' + abbreviations.WHO);

    }));

    it('can save model value', inject(function(editor, $rootScope, spellcheck, $q) {
        spyOn(spellcheck, 'getAbbreviationsDict').and.returnValue($q.when({}));
        var scope = createScope('foo', $rootScope);
        editor.registerScope(scope);

        scope.node.innerHTML = 'foo';
        editor.commit();
        expect(scope.model.$setViewValue).not.toHaveBeenCalled();

        editor.undo(scope);
        expect(scope.model.$setViewValue).not.toHaveBeenCalled();
        editor.redo(scope);
        expect(scope.model.$setViewValue).not.toHaveBeenCalled();

        scope.node.innerHTML = 'bar';
        editor.commit();
        $rootScope.$digest();
        expect(scope.model.$setViewValue).toHaveBeenCalledWith('bar');

        scope.node.innerHTML = 'baz';
        editor.commit();
        $rootScope.$digest();
        editor.undo(scope);
        editor.undo(scope);
        expect(scope.node.innerHTML).toBe('foo');

        editor.redo(scope);
        expect(scope.node.innerHTML).toBe('bar');

        editor.redo(scope);
        expect(scope.node.innerHTML).toBe('baz');

        editor.undo(scope);
        editor.undo(scope);

        scope.node.innerHTML = 'test';
        editor.commit();
        $rootScope.$digest();
        editor.redo(scope);
        expect(scope.node.innerHTML).toBe('test');
    }));

    it('can check if keyboard event is important or not', inject(function(editor) {
        expect(editor.shouldIgnore({keyCode: 16})).toBe(true);
        expect(editor.shouldIgnore({keyCode: 39})).toBe(true);
        expect(editor.shouldIgnore({shiftKey: true, ctrlKey: true, keyCode: 65})).toBe(true);
        expect(editor.shouldIgnore({keyCode: 65})).toBe(false);
    }));

    it('can observe and apply disableEditorToolbar configuration option',
    inject(function($rootScope, $compile, config) {
        var scope = $rootScope.$new();

        config.editor = {
            disableEditorToolbar: true
        };

        scope.item = {
            body_html: '
test
',
            type: 'text'
        };

        scope.node = document.createElement('p');
        scope.node.innerHTML = 'test';

        var $elm = $compile('

')(scope);
        scope.$digest();

        var isoScope = $elm.isolateScope();

        isoScope.$digest();

        expect(window.MediumEditor).toHaveBeenCalled();
        expect(window.MediumEditor.calls.argsFor(0)[1].disableToolbar).
            toBe(config.editor.disableEditorToolbar);
    }));

    it('can observe and set tabindex from data-config if defined',
    inject(function($rootScope, $compile, config) {
        var scope = $rootScope.$new();

        scope.item = {
            body_html: '
test
',
            type: 'text'
        };

        scope.node = document.createElement('p');
        scope.node.innerHTML = 'test';

        var $elm = $compile('

')(scope);
        scope.$digest();

        var isoScope = $elm.isolateScope();

        isoScope.$digest();

        expect(isoScope.config.tabindex).toBe(1);
        expect(window.MediumEditor).toHaveBeenCalled();
    }));

});
<|endoftext|>


<commit_before>var OsomDatepicker = (function(){

	"use strict";

	var OsomDatepickerPrivate = function(options){
		this.options = options;
		this.selectedDates = this.options.selectedDates || [new Date()];
		this.selectedDates.forEach(function(date){
			Helper.resetTime(date);
		});
		this.currentMonth = 0;
		this.centralMonth = 0;
		this.firstMonth = -1;
		this.lastMonth = 1;
		this.multipleDays = this.selectedDates.length > 1 ? true : false;
		this.uniqueId = Date.now();
	};

	OsomDatepickerPrivate.prototype = {

		wrapperClass: 'osom-datepicker-wrapper',
		containerClass: 'osom-datepicker-container',
		sliderClass: 'osom-datepicker-slider',
		monthClass: 'osom-datepicker-month',
		dayClass: 'osom-datepicker-day',
		dayDisabledClass: 'osom-datepicker-daydisabled',
		selectedDayClass: 'osom-datepicker-selectedday',
		animatedClass: 'osom-datepicker-animated',
		upButtonClass: 'osom-datepicker-upbutton',
		downButtonClass: 'osom-datepicker-downbutton',
		multipleDaysClass: 'osom-datepicker-multipledays',
		layoutVerticalClass: 'osom-datepicker-layoutvertical',
		layoutHorizontalClass: 'osom-datepicker-layouthorizontal',

		events: {
			DATE_SELECTED: 'DATE_SELECTED'
		},

		initialize: function(){
			var html = '';
			this.el = document.querySelector(this.options.selector);

			this.el.classList.add(this.wrapperClass);
			if(this.options.animation === 'horizontal'){
				this.el.classList.add(this.layoutHorizontalClass);
			}else{
				this.el.classList.add(this.layoutVerticalClass);
			}

			html += '
';

			html += '<input type="checkbox" class="' + this.multipleDaysClass + '" name="' + this.multipleDaysClass + '" id="' + this.multipleDaysClass + '-' + this.uniqueId + '" ' + (this.multipleDays ? 'checked="checked"' : '') + ' />';

			html += 'Multiple Days';
			if(this.options.animation === 'horizontal'){
				html += '<button class="' + this.upButtonClass + '">◄</button>';
				html += '<button class="' + this.downButtonClass + '">►</button>';
			}else{
				html += '<button class="' + this.upButtonClass + '">▲</button>';
				html += '<button class="' + this.downButtonClass + '">▼</button>';
			}
			html += '
';

			html += '
';
			html += '
';
			for(var i = -1; i <= 1; i++){
				var date = Helper.addMonth(this.selectedDates[0], i);
				html += this.renderMonth(date);
			}
			html += '
';
			html += '
';

			this.el.innerHTML = html;

			this.bindEvents();
		},

		renderMonth: function(date){
			var html = '';
			var year = date.getMonth()+1 <= 11 ? date.getFullYear() : date.getFullYear()+1;
			var month = date.getMonth()+1 <= 11 ? date.getMonth()+1 : 0;
			var auxDate = new Date(year, month, 0);

			html += '
' + Helper.getMonthName(date.getMonth()) + ' ' + date.getFullYear() + '
';

			html += '
';
			html += 'Mon	Tue	Wed	Thu	Fri	Sat	Sun	';
			html += '
';

			html += '
';
			var dayNumber = 0;
			for(var week = 1; week <= 6; week++){
				var dayOfWeek = 0;
				html += '
';
				do{
					var day = new Date(date.getFullYear(), date.getMonth(), dayNumber+1);
					if(day.getDay() === dayOfWeek && dayNumber <= day.getDate()){
						var classes = this.dayClass;
						html += '';

						if(this.options.fromDate){
							Helper.resetTime(this.options.fromDate);
							if(day < this.options.fromDate){
								classes += ' ' + this.dayDisabledClass;
							}
						}

						if(this.selectedDates.map(Number).indexOf(+day) !== -1){
							classes += ' ' + this.selectedDayClass;
						}

						html += '<button class="' + classes + '" data-date="' + day.getTime() + '">' + day.getDate() + '</button>';
						html += '	';
						dayNumber++;
					}else{
						html += ' 	';
					}
					dayOfWeek++;
				}while(dayOfWeek <= 6);
				html += '
';
			}
			html += '
';
			html += '

';

			return html;
		},

		prevMonth: function(){
			if(!this.animating){
				var self = this;
				this.animating = true;

				this.currentMonth--;
				var slider = this.el.querySelector('.' + this.sliderClass);
				var monthWidth = slider.offsetWidth / slider.children.length;
				var monthHeight = slider.offsetHeight / slider.children.length;
				var sliderStyle = window.getComputedStyle(slider, null);

				if(this.currentMonth < this.firstMonth){
					this.firstMonth = this.currentMonth;

					var date = Helper.addMonth(this.selectedDates[0], this.currentMonth);
					slider.insertAdjacentHTML('afterbegin', this.renderMonth(date));
					if(this.options.animation === 'horizontal'){
						slider.style.left = (parseInt(sliderStyle.left, 10) - monthWidth) + 'px';
						//FORCING REPAINT
						slider.offsetWidth;
					}else{
						slider.style.top = (parseInt(sliderStyle.top, 10) - monthHeight) + 'px';
						//FORCING REPAINT
						slider.offsetHeight;
					}
				}

				slider.classList.add(this.animatedClass);
				if(this.options.animation === 'horizontal'){
					slider.style.left = parseInt(slider.style.left || sliderStyle.left, 10) + monthWidth + 'px';
				}else{
					slider.style.top = parseInt(slider.style.top || sliderStyle.top, 10) + monthHeight + 'px';
				}

				setTimeout(function(){
					self.animating = false;
					slider.classList.remove(self.animatedClass);
				}, 1000);
			}
		},

		nextMonth: function(){
			if(!this.animating){
				var self = this;
				this.animating = true;

				this.currentMonth++;
				var slider = this.el.querySelector('.' + this.sliderClass);
				var monthWidth = slider.offsetWidth / slider.children.length;
				var monthHeight = slider.offsetHeight / slider.children.length;
				var sliderStyle = window.getComputedStyle(slider, null);

				if(this.currentMonth > this.lastMonth){
					this.lastMonth = this.currentMonth;

					var date = Helper.addMonth(this.selectedDates[0], this.currentMonth);
					slider.insertAdjacentHTML('beforeend', this.renderMonth(date));
				}

				slider.classList.add(this.animatedClass);
				if(this.options.animation === 'horizontal'){
					slider.style.left = parseInt(slider.style.left || sliderStyle.left, 10) - monthWidth + 'px';
				}else{
					slider.style.top = parseInt(slider.style.top || sliderStyle.top, 10) - monthHeight + 'px';
				}
				setTimeout(function(){
					self.animating = false;
					slider.classList.remove(self.animatedClass);
				}, 800);
			}
		},

		bindEvents: function(){
			var self = this;
			var upButton = this.el.querySelector('.'+this.upButtonClass);
			var downButton = this.el.querySelector('.'+this.downButtonClass);
			var slider = this.el.querySelector('.' + this.sliderClass);
			var multipleDays = this.el.querySelector('.' + this.multipleDaysClass);

			upButton.addEventListener('click', function(){
				self.prevMonth();
			});

			downButton.addEventListener('click', function(){
				self.nextMonth();
			});

			multipleDays.addEventListener('change', function(){
				self.toggleMultipleDays();
			});

			slider.addEventListener('click', function(e){
				if(e.target.classList.contains(self.dayClass) && !e.target.classList.contains(self.dayDisabledClass)){
					self.handleDayClick(e.target);
				}
			});
		},

		handleDayClick: function(day){
			var self = this;

			if(!this.multipleDays){
				var days = this.el.querySelectorAll('.' + this.selectedDayClass);

				Array.prototype.forEach.call(days, function(d){
					d.classList.remove(self.selectedDayClass);
				});

				this.selectedDates = [new Date(parseInt(day.dataset.date, 10))];
			}else{
				this.selectedDates.push(new Date(parseInt(day.dataset.date, 10)));
			}

			day.classList.toggle(this.selectedDayClass);

			this.dispatchEvent(this.events.DATE_SELECTED, this.selectedDates);
		},

		toggleMultipleDays: function(){
			this.multipleDays = !this.multipleDays;

			if(this.selectedDates.length > 1){
				var day = this.el.querySelector('button[data-date="' + this.selectedDates[0].getTime() + '"]');
				this.handleDayClick(day);
			}
		},

		dispatchEvent: function(type, data){
			var e = new CustomEvent(type, {
				detail: data
			});
			this.el.dispatchEvent(e);
		},

		on: function(type, callback){
			this.el.addEventListener(type, callback);
		},

		off: function(type, callback){
			this.el.removeEventListener(type, callback);
		},

		setDates: function(dates){
			var self = this;
			var checkbox = this.el.querySelector('.' + this.multipleDaysClass);
			var days = this.el.querySelectorAll('.' + this.selectedDayClass);

			Array.prototype.forEach.call(days, function(d){
				d.classList.remove(self.selectedDayClass);
			});

			if(dates.length > 1){
				this.multipleDays = true;
				checkbox.checked = true;
			}else{
				this.multipleDays = false;
				checkbox.checked = false;
			}
			this.selectedDates = dates;

			this.selectedDates.forEach(function(date){
				Helper.resetTime(date);
				var day = self.el.querySelector('button[data-date="' + date.getTime() + '"]');
				day.classList.toggle(self.selectedDayClass);
			});

			this.dispatchEvent(this.events.DATE_SELECTED, this.selectedDates);
		}

	};

	//HELPER
	var Helper = {
		addMonth: function(date, add){
			var newDate = new Date(date.getTime());
			newDate.setDate(1);
			newDate.setMonth(date.getMonth()+add);
			newDate.setDate(date.getDate());

			return newDate;
		},

		getMonthName: function(month){
			var monthNames = [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ];
			return monthNames[month];
		},

		resetTime: function(date){
			date.setHours(0);
			date.setMinutes(0);
			date.setSeconds(0);
			date.setMilliseconds(0);
		}
	};

	//PUBLIC API
	var datepickerInstance = null;
	var OsomDatepickerPublic = function(options){
		datepickerInstance = new OsomDatepickerPrivate(options);
	}

	OsomDatepickerPublic.prototype = {

		initialize: function(){
			datepickerInstance.initialize();
		},

		on: function(type, callback){
			datepickerInstance.on(type, callback);
		},

		off: function(type, callback){
			datepickerInstance.off(type, callback);
		},

		setDates: function(dates){
			datepickerInstance.setDates(dates);
		}

	};

	return OsomDatepickerPublic;

});

if ( typeof define === "function" && define.amd ) {
	define( "osomdatepicker", [], function() {
		return OsomDatepicker();
	});
}else{
	OsomDatepicker = OsomDatepicker();
}<commit_msg>Adding support for hiding multiple days<commit_after>var OsomDatepicker = (function(){

	"use strict";

	var OsomDatepickerPrivate = function(options){
		this.options = options;
		this.selectedDates = this.options.selectedDates || [new Date()];
		this.selectedDates.forEach(function(date){
			Helper.resetTime(date);
		});
		this.currentMonth = 0;
		this.centralMonth = 0;
		this.firstMonth = -1;
		this.lastMonth = 1;
		this.multipleDays = this.selectedDates.length > 1 ? true : false;
		this.uniqueId = Date.now();
	};

	OsomDatepickerPrivate.prototype = {

		wrapperClass: 'osom-datepicker-wrapper',
		containerClass: 'osom-datepicker-container',
		sliderClass: 'osom-datepicker-slider',
		monthClass: 'osom-datepicker-month',
		dayClass: 'osom-datepicker-day',
		dayDisabledClass: 'osom-datepicker-daydisabled',
		selectedDayClass: 'osom-datepicker-selectedday',
		animatedClass: 'osom-datepicker-animated',
		upButtonClass: 'osom-datepicker-upbutton',
		downButtonClass: 'osom-datepicker-downbutton',
		multipleDaysClass: 'osom-datepicker-multipledays',
		layoutVerticalClass: 'osom-datepicker-layoutvertical',
		layoutHorizontalClass: 'osom-datepicker-layouthorizontal',

		events: {
			DATE_SELECTED: 'DATE_SELECTED'
		},

		initialize: function(){
			var html = '';
			this.el = document.querySelector(this.options.selector);

			this.el.classList.add(this.wrapperClass);
			if(this.options.animation === 'horizontal'){
				this.el.classList.add(this.layoutHorizontalClass);
			}else{
				this.el.classList.add(this.layoutVerticalClass);
			}

			html += '
';

			if(this.showMultipleDays){
				html += '<input type="checkbox" class="' + this.multipleDaysClass + '" name="' + this.multipleDaysClass + '" id="' + this.multipleDaysClass + '-' + this.uniqueId + '" ' + (this.multipleDays ? 'checked="checked"' : '') + ' />';
				html += 'Multiple Days';
			}

			if(this.options.animation === 'horizontal'){
				html += '<button class="' + this.upButtonClass + '">◄</button>';
				html += '<button class="' + this.downButtonClass + '">►</button>';
			}else{
				html += '<button class="' + this.upButtonClass + '">▲</button>';
				html += '<button class="' + this.downButtonClass + '">▼</button>';
			}
			html += '
';

			html += '
';
			html += '
';
			for(var i = -1; i <= 1; i++){
				var date = Helper.addMonth(this.selectedDates[0], i);
				html += this.renderMonth(date);
			}
			html += '
';
			html += '
';

			this.el.innerHTML = html;

			this.bindEvents();
		},

		renderMonth: function(date){
			var html = '';
			var year = date.getMonth()+1 <= 11 ? date.getFullYear() : date.getFullYear()+1;
			var month = date.getMonth()+1 <= 11 ? date.getMonth()+1 : 0;
			var auxDate = new Date(year, month, 0);

			html += '
' + Helper.getMonthName(date.getMonth()) + ' ' + date.getFullYear() + '
';

			html += '
';
			html += 'Mon	Tue	Wed	Thu	Fri	Sat	Sun	';
			html += '
';

			html += '
';
			var dayNumber = 0;
			for(var week = 1; week <= 6; week++){
				var dayOfWeek = 0;
				html += '
';
				do{
					var day = new Date(date.getFullYear(), date.getMonth(), dayNumber+1);
					if(day.getDay() === dayOfWeek && dayNumber <= day.getDate()){
						var classes = this.dayClass;
						html += '';

						if(this.options.fromDate){
							Helper.resetTime(this.options.fromDate);
							if(day < this.options.fromDate){
								classes += ' ' + this.dayDisabledClass;
							}
						}

						if(this.selectedDates.map(Number).indexOf(+day) !== -1){
							classes += ' ' + this.selectedDayClass;
						}

						html += '<button class="' + classes + '" data-date="' + day.getTime() + '">' + day.getDate() + '</button>';
						html += '	';
						dayNumber++;
					}else{
						html += ' 	';
					}
					dayOfWeek++;
				}while(dayOfWeek <= 6);
				html += '
';
			}
			html += '
';
			html += '

';

			return html;
		},

		prevMonth: function(){
			if(!this.animating){
				var self = this;
				this.animating = true;

				this.currentMonth--;
				var slider = this.el.querySelector('.' + this.sliderClass);
				var monthWidth = slider.offsetWidth / slider.children.length;
				var monthHeight = slider.offsetHeight / slider.children.length;
				var sliderStyle = window.getComputedStyle(slider, null);

				if(this.currentMonth < this.firstMonth){
					this.firstMonth = this.currentMonth;

					var date = Helper.addMonth(this.selectedDates[0], this.currentMonth);
					slider.insertAdjacentHTML('afterbegin', this.renderMonth(date));
					if(this.options.animation === 'horizontal'){
						slider.style.left = (parseInt(sliderStyle.left, 10) - monthWidth) + 'px';
						//FORCING REPAINT
						slider.offsetWidth;
					}else{
						slider.style.top = (parseInt(sliderStyle.top, 10) - monthHeight) + 'px';
						//FORCING REPAINT
						slider.offsetHeight;
					}
				}

				slider.classList.add(this.animatedClass);
				if(this.options.animation === 'horizontal'){
					slider.style.left = parseInt(slider.style.left || sliderStyle.left, 10) + monthWidth + 'px';
				}else{
					slider.style.top = parseInt(slider.style.top || sliderStyle.top, 10) + monthHeight + 'px';
				}

				setTimeout(function(){
					self.animating = false;
					slider.classList.remove(self.animatedClass);
				}, 1000);
			}
		},

		nextMonth: function(){
			if(!this.animating){
				var self = this;
				this.animating = true;

				this.currentMonth++;
				var slider = this.el.querySelector('.' + this.sliderClass);
				var monthWidth = slider.offsetWidth / slider.children.length;
				var monthHeight = slider.offsetHeight / slider.children.length;
				var sliderStyle = window.getComputedStyle(slider, null);

				if(this.currentMonth > this.lastMonth){
					this.lastMonth = this.currentMonth;

					var date = Helper.addMonth(this.selectedDates[0], this.currentMonth);
					slider.insertAdjacentHTML('beforeend', this.renderMonth(date));
				}

				slider.classList.add(this.animatedClass);
				if(this.options.animation === 'horizontal'){
					slider.style.left = parseInt(slider.style.left || sliderStyle.left, 10) - monthWidth + 'px';
				}else{
					slider.style.top = parseInt(slider.style.top || sliderStyle.top, 10) - monthHeight + 'px';
				}
				setTimeout(function(){
					self.animating = false;
					slider.classList.remove(self.animatedClass);
				}, 800);
			}
		},

		bindEvents: function(){
			var self = this;
			var upButton = this.el.querySelector('.'+this.upButtonClass);
			var downButton = this.el.querySelector('.'+this.downButtonClass);
			var slider = this.el.querySelector('.' + this.sliderClass);
			var multipleDays = this.el.querySelector('.' + this.multipleDaysClass);

			upButton.addEventListener('click', function(){
				self.prevMonth();
			});

			downButton.addEventListener('click', function(){
				self.nextMonth();
			});

			multipleDays.addEventListener('change', function(){
				self.toggleMultipleDays();
			});

			slider.addEventListener('click', function(e){
				if(e.target.classList.contains(self.dayClass) && !e.target.classList.contains(self.dayDisabledClass)){
					self.handleDayClick(e.target);
				}
			});
		},

		handleDayClick: function(day){
			var self = this;

			if(!this.multipleDays){
				var days = this.el.querySelectorAll('.' + this.selectedDayClass);

				Array.prototype.forEach.call(days, function(d){
					d.classList.remove(self.selectedDayClass);
				});

				this.selectedDates = [new Date(parseInt(day.dataset.date, 10))];
			}else{
				this.selectedDates.push(new Date(parseInt(day.dataset.date, 10)));
			}

			day.classList.toggle(this.selectedDayClass);

			this.dispatchEvent(this.events.DATE_SELECTED, this.selectedDates);
		},

		toggleMultipleDays: function(){
			this.multipleDays = !this.multipleDays;

			if(this.selectedDates.length > 1){
				var day = this.el.querySelector('button[data-date="' + this.selectedDates[0].getTime() + '"]');
				this.handleDayClick(day);
			}
		},

		dispatchEvent: function(type, data){
			var e = new CustomEvent(type, {
				detail: data
			});
			this.el.dispatchEvent(e);
		},

		on: function(type, callback){
			this.el.addEventListener(type, callback);
		},

		off: function(type, callback){
			this.el.removeEventListener(type, callback);
		},

		setDates: function(dates){
			var self = this;
			var checkbox = this.el.querySelector('.' + this.multipleDaysClass);
			var days = this.el.querySelectorAll('.' + this.selectedDayClass);

			Array.prototype.forEach.call(days, function(d){
				d.classList.remove(self.selectedDayClass);
			});

			if(dates.length > 1){
				this.multipleDays = true;
				checkbox.checked = true;
			}else{
				this.multipleDays = false;
				checkbox.checked = false;
			}
			this.selectedDates = dates;

			this.selectedDates.forEach(function(date){
				Helper.resetTime(date);
				var day = self.el.querySelector('button[data-date="' + date.getTime() + '"]');
				day.classList.toggle(self.selectedDayClass);
			});

			this.dispatchEvent(this.events.DATE_SELECTED, this.selectedDates);
		}

	};

	//HELPER
	var Helper = {
		addMonth: function(date, add){
			var newDate = new Date(date.getTime());
			newDate.setDate(1);
			newDate.setMonth(date.getMonth()+add);
			newDate.setDate(date.getDate());

			return newDate;
		},

		getMonthName: function(month){
			var monthNames = [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ];
			return monthNames[month];
		},

		resetTime: function(date){
			date.setHours(0);
			date.setMinutes(0);
			date.setSeconds(0);
			date.setMilliseconds(0);
		}
	};

	//PUBLIC API
	var datepickerInstance = null;
	var OsomDatepickerPublic = function(options){
		datepickerInstance = new OsomDatepickerPrivate(options);
	}

	OsomDatepickerPublic.prototype = {

		initialize: function(){
			datepickerInstance.initialize();
		},

		on: function(type, callback){
			datepickerInstance.on(type, callback);
		},

		off: function(type, callback){
			datepickerInstance.off(type, callback);
		},

		setDates: function(dates){
			datepickerInstance.setDates(dates);
		}

	};

	return OsomDatepickerPublic;

});

if ( typeof define === "function" && define.amd ) {
	define( "osomdatepicker", [], function() {
		return OsomDatepicker();
	});
}else{
	OsomDatepicker = OsomDatepicker();
}<|endoftext|>


<commit_before>const _ = require('lodash')
const log = require('@frontiernav/logger').get(__filename)
const Collectibles = require('./Collectibles')
const Accessories = require('./Accessories')
const CoreChips = require('./CoreChips')
const KeyItems = require('./KeyItems')
const UnrefinedAuxCores = require('./UnrefinedAuxCores')
const CoreCrystals = require('./CoreCrystals')
const Boosters = require('./Boosters')
const { nameToId } = require('@frontiernav/graph')
const { getAllRaw, getAllRawByName } = require('../util/gimmicks')

const ItemTypes = [Collectibles, Accessories, CoreCrystals, UnrefinedAuxCores, CoreChips, KeyItems, Boosters]

const getDrops = async (rawTreasurePoint) => {
  const data = [
    {
      id: rawTreasurePoint.itm1ID,
      rate: 100,
      count: rawTreasurePoint.itm1Num
    },
    {
      id: rawTreasurePoint.itm2ID,
      rate: 100,
      count: rawTreasurePoint.itm2Num
    },
    {
      id: rawTreasurePoint.itm3ID,
      rate: 100,
      count: rawTreasurePoint.itm3Num
    },
    {
      id: rawTreasurePoint.itm4ID,
      rate: 100,
      count: rawTreasurePoint.itm4Num
    },
    {
      id: rawTreasurePoint.itm5ID,
      rate: 100,
      count: rawTreasurePoint.itm5Num
    },
    {
      id: rawTreasurePoint.itm6ID,
      rate: 100,
      count: rawTreasurePoint.itm6Num
    },
    {
      id: rawTreasurePoint.itm7ID,
      rate: 100,
      count: rawTreasurePoint.itm7Num
    },
    {
      id: rawTreasurePoint.itm8ID,
      rate: 100,
      count: rawTreasurePoint.itm8Num
    }
  ]

  return Promise
    .all(
      data
        .filter(drop => !!drop.id)
        .map(drop => {
          return ItemTypes
            .reduce((chain, type) => {
              return chain
                .catch(() => type.getById(drop.id))
            }, Promise.reject(new Error('No types to search through.')))
            .catch(error => {
              console.error(`TreasureBox[${rawTreasurePoint.id}] Item[${drop.id}] not found.`, error)
              console.dir(rawTreasurePoint)
              process.exit(1)
            })
            .then(item => item.name)
            .then(name => nameToId(name))
            .then(id => ({
              id,
              rate: drop.rate
            }))
        })
    )
    .then(drops => drops
      .filter(d => d)
      .reduce((acc, next) => {
        const existing = acc[next.id]
        const nextRate = { rate: next.rate }
        if (existing) {
          existing.rates.push(nextRate)
        } else {
          acc[next.id] = {
            id: next.id,
            rates: [nextRate]
          }
        }
        return acc
      }, {})
    )
    .then(dict => _.values(dict))
}

/**
 * TODO: Process FSID and FSID2 (FLD_FieldSkillSetting)
 */
const toTreasurePoint = _.memoize(async raw => {
  const drops = await getDrops(raw)
  return {
    name: `Treasure Point #${raw.id}`,
    min_gold: raw.goldMin,
    max_gold: raw.goldMax,
    drops
  }
}, raw => raw.name)

exports.getByName = async ({ name }) => {
  const rawTreasurePoints = await getAllRawByName({ type: 'FLD_TboxPop' })
  const rawTreasurePoint = rawTreasurePoints[`${name}`]
  if (!rawTreasurePoint) {
    throw new Error(`TreasurePoint[${name}] not found`)
  }
  return toTreasurePoint(rawTreasurePoint)
}

exports.getAll = async () => {
  const rawTreasurePoints = await getAllRaw('FLD_TboxPop')
  return Promise
    .all(
      rawTreasurePoints.map(raw => (
        toTreasurePoint(raw)
          .catch(e => {
            log.warn(e.message)
            return null
          })
      ))
    )
    .then(results => results.filter(r => r))
}
<commit_msg>fix(xc2): include drop counts for treasure points<commit_after>const _ = require('lodash')
const log = require('@frontiernav/logger').get(__filename)
const Collectibles = require('./Collectibles')
const Accessories = require('./Accessories')
const CoreChips = require('./CoreChips')
const KeyItems = require('./KeyItems')
const UnrefinedAuxCores = require('./UnrefinedAuxCores')
const CoreCrystals = require('./CoreCrystals')
const Boosters = require('./Boosters')
const { nameToId } = require('@frontiernav/graph')
const { getAllRaw, getAllRawByName } = require('../util/gimmicks')

const ItemTypes = [Collectibles, Accessories, CoreCrystals, UnrefinedAuxCores, CoreChips, KeyItems, Boosters]

const getDrops = async (rawTreasurePoint) => {
  const data = [
    {
      id: rawTreasurePoint.itm1ID,
      rate: 100,
      count: rawTreasurePoint.itm1Num
    },
    {
      id: rawTreasurePoint.itm2ID,
      rate: 100,
      count: rawTreasurePoint.itm2Num
    },
    {
      id: rawTreasurePoint.itm3ID,
      rate: 100,
      count: rawTreasurePoint.itm3Num
    },
    {
      id: rawTreasurePoint.itm4ID,
      rate: 100,
      count: rawTreasurePoint.itm4Num
    },
    {
      id: rawTreasurePoint.itm5ID,
      rate: 100,
      count: rawTreasurePoint.itm5Num
    },
    {
      id: rawTreasurePoint.itm6ID,
      rate: 100,
      count: rawTreasurePoint.itm6Num
    },
    {
      id: rawTreasurePoint.itm7ID,
      rate: 100,
      count: rawTreasurePoint.itm7Num
    },
    {
      id: rawTreasurePoint.itm8ID,
      rate: 100,
      count: rawTreasurePoint.itm8Num
    }
  ]

  return Promise
    .all(
      data
        .filter(drop => !!drop.id)
        .map(drop => {
          return ItemTypes
            .reduce((chain, type) => {
              return chain
                .catch(() => type.getById(drop.id))
            }, Promise.reject(new Error('No types to search through.')))
            .catch(error => {
              console.error(`TreasureBox[${rawTreasurePoint.id}] Item[${drop.id}] not found.`, error)
              console.dir(rawTreasurePoint)
              process.exit(1)
            })
            .then(item => item.name)
            .then(name => nameToId(name))
            .then(id => ({
              id,
              rate: drop.rate,
              count: drop.count
            }))
        })
    )
    .then(drops => drops
      .filter(d => d)
      .reduce((acc, next) => {
        const existing = acc[next.id]
        const nextRate = {
          rate: next.rate,
          count: next.count
        }
        if (existing) {
          existing.rates.push(nextRate)
        } else {
          acc[next.id] = {
            id: next.id,
            rates: [nextRate]
          }
        }
        return acc
      }, {})
    )
    .then(dict => _.values(dict))
}

/**
 * TODO: Process FSID and FSID2 (FLD_FieldSkillSetting)
 */
const toTreasurePoint = _.memoize(async raw => {
  const drops = await getDrops(raw)
  return {
    name: `Treasure Point #${raw.id}`,
    min_gold: raw.goldMin,
    max_gold: raw.goldMax,
    drops
  }
}, raw => raw.name)

exports.getByName = async ({ name }) => {
  const rawTreasurePoints = await getAllRawByName({ type: 'FLD_TboxPop' })
  const rawTreasurePoint = rawTreasurePoints[`${name}`]
  if (!rawTreasurePoint) {
    throw new Error(`TreasurePoint[${name}] not found`)
  }
  return toTreasurePoint(rawTreasurePoint)
}

exports.getAll = async () => {
  const rawTreasurePoints = await getAllRaw('FLD_TboxPop')
  return Promise
    .all(
      rawTreasurePoints.map(raw => (
        toTreasurePoint(raw)
          .catch(e => {
            log.warn(e.message)
            return null
          })
      ))
    )
    .then(results => results.filter(r => r))
}
<|endoftext|>


<commit_before>const chalk = require('chalk');
const fs = require('fs-extra');
const path = require('path');
const reporter = require('./reporter');
const url = require('url');

const ASSET_PATTERN = /\.(mp4|m4v|png|svg|jpe?g|gif)$/i;
const GITHUB_BASE_PATH = '/material-components/material-components';

function rewriteLocalLinks(file, site, srcPathsToFiles) {
  const linkModifications = determineLinkModifications(file, site, srcPathsToFiles);
  for (const [varName, newUrl] of linkModifications) {
    file.localLinkTemplateVars.set(varName, newUrl);
  }
}

function determineLinkModifications(file, site, srcPathsToFiles) {
  const modifications = new Map();
  for (const [varName, href] of file.localLinkTemplateVars) {
    const newHref = processHref(href, file, site, srcPathsToFiles);
    if (newHref) {
      modifications.set(varName, newHref);
    }
  }
  return modifications;
}

function processHref(href, file, site, srcPathsToFiles) {
  const srcLocalUrl = url.parse(path.resolve(file.originalDir, href));

  // If the link appears to be an absolute GitHub URL, add the hostname.
  if (srcLocalUrl.pathname.startsWith(GITHUB_BASE_PATH)) {
    return `https://www.github.com${ srcLocalUrl.format() }`;
  }

  // If the resolved path falls outside the MDC repo directory, or the file
  // doesn't exist, assume it's an example link and return.
  if (!srcLocalUrl.pathname.startsWith(site.repoPath) ||
      !fs.pathExistsSync(srcLocalUrl.pathname)) {
    return null;
  }

  // If we're dealing with an asset, copy it over
  if (ASSET_PATTERN.test(srcLocalUrl.pathname)) {
    const destLocalPath = path.resolve(file.dirname, href);
    fs.copySync(srcLocalUrl.pathname, destLocalPath);
    return null;
  }

  const pathWithReadme = path.join(srcLocalUrl.pathname, 'README.md');
  if (srcPathsToFiles.has(pathWithReadme)) {
    srcLocalUrl.pathname = pathWithReadme;
  }

  // If the specified path has an associated markdown file in the site, rewrite
  // the link to point to it, including the search and has.
  if (srcPathsToFiles.has(srcLocalUrl.pathname)) {
    const destFile = srcPathsToFiles.get(srcLocalUrl.pathname);
    const destLocalUrl = url.parse(path.join(site.basepath, destFile.jekyllMetadata.path));
    destLocalUrl.search = srcLocalUrl.search;
    destLocalUrl.hash = srcLocalUrl.hash;
    return destLocalUrl.format();
  }

  reporter.linkWarning(href, file.path, 'No local link found. Writing GitHub link.')

  const githubUrl = url.parse(site.repoUrl, srcLocalUrl.pathname);
  githubUrl.pathname = path.join(githubUrl.pathname, 'tree/master', srcLocalUrl.pathname.replace(site.repoPath, ''));
  githubUrl.search = srcLocalUrl.search;
  githubUrl.hash = srcLocalUrl.hash;
  return githubUrl.format();
}

module.exports = {
  rewriteLocalLinks,
};
<commit_msg>Adds a TODO around some strangeness.<commit_after>const chalk = require('chalk');
const fs = require('fs-extra');
const path = require('path');
const reporter = require('./reporter');
const url = require('url');

const ASSET_PATTERN = /\.(mp4|m4v|png|svg|jpe?g|gif)$/i;
const GITHUB_BASE_PATH = '/material-components/material-components';

function rewriteLocalLinks(file, site, srcPathsToFiles) {
  const linkModifications = determineLinkModifications(file, site, srcPathsToFiles);
  for (const [varName, newUrl] of linkModifications) {
    file.localLinkTemplateVars.set(varName, newUrl);
  }
}

function determineLinkModifications(file, site, srcPathsToFiles) {
  const modifications = new Map();
  for (const [varName, href] of file.localLinkTemplateVars) {
    const newHref = processHref(href, file, site, srcPathsToFiles);
    if (newHref) {
      modifications.set(varName, newHref);
    }
  }
  return modifications;
}

function processHref(href, file, site, srcPathsToFiles) {
  const srcLocalUrl = url.parse(path.resolve(file.originalDir, href));

  // If the link appears to be an absolute GitHub URL, add the hostname.
  if (srcLocalUrl.pathname.startsWith(GITHUB_BASE_PATH)) {
    return `https://www.github.com${ srcLocalUrl.format() }`;
  }

  // If the resolved path falls outside the MDC repo directory, or the file
  // doesn't exist, assume it's an example link and return.
  if (!srcLocalUrl.pathname.startsWith(site.repoPath) ||
      !fs.pathExistsSync(srcLocalUrl.pathname)) {
    return null;
  }

  // If we're dealing with an asset, copy it over.
  // TODO(shyndman): This feels out of place here. These methods are about
  // rewriting links, not copying assets.
  if (ASSET_PATTERN.test(srcLocalUrl.pathname)) {
    const destLocalPath = path.resolve(file.dirname, href);
    fs.copySync(srcLocalUrl.pathname, destLocalPath);
    return null;
  }

  const pathWithReadme = path.join(srcLocalUrl.pathname, 'README.md');
  if (srcPathsToFiles.has(pathWithReadme)) {
    srcLocalUrl.pathname = pathWithReadme;
  }

  // If the specified path has an associated markdown file in the site, rewrite
  // the link to point to it, including the search and has.
  if (srcPathsToFiles.has(srcLocalUrl.pathname)) {
    const destFile = srcPathsToFiles.get(srcLocalUrl.pathname);
    const destLocalUrl = url.parse(path.join(site.basepath, destFile.jekyllMetadata.path));
    destLocalUrl.search = srcLocalUrl.search;
    destLocalUrl.hash = srcLocalUrl.hash;
    return destLocalUrl.format();
  }

  reporter.linkWarning(href, file.path, 'No local link found. Writing GitHub link.')

  const githubUrl = url.parse(site.repoUrl, srcLocalUrl.pathname);
  githubUrl.pathname = path.join(githubUrl.pathname, 'tree/master', srcLocalUrl.pathname.replace(site.repoPath, ''));
  githubUrl.search = srcLocalUrl.search;
  githubUrl.hash = srcLocalUrl.hash;
  return githubUrl.format();
}

module.exports = {
  rewriteLocalLinks,
};
<|endoftext|>


<commit_before>angular.module('app.controllers')

.controller('billCtrl', function($scope, BillService, ionicToast) {

  $scope.bill = BillService.getBill();

  $scope.addBill = function(bill, event) {

    var tempBill = angular.copy(bill);

    if(tempBill.totalAmount == undefined || tempBill.totalAmount < 0 ) {
			ionicToast.show('The amount is invalid.', 'top', false, 2500);
			event.stopPropagation();
			event.preventDefault();
		} else{
		    BillService.setBill(angular.copy(bill));
    }
	};

  $scope.calculateGrandTotal = function(bill) {
  	BillService.setBill(angular.copy(bill));
    $scope.bill = BillService.calculateGrandTotal();
  };

});
<commit_msg>Prevent zero total amount<commit_after>angular.module('app.controllers')

.controller('billCtrl', function($scope, BillService, ionicToast) {

  $scope.bill = BillService.getBill();

  $scope.addBill = function(bill, event) {

    var tempBill = angular.copy(bill);

    if(tempBill.totalAmount == undefined || isNaN(tempBill.totalAmount) || tempBill.totalAmount <= 0) {
			ionicToast.show('The amount is invalid.', 'top', false, 2500);
			event.stopPropagation();
			event.preventDefault();
		} else{
		    BillService.setBill(angular.copy(bill));
    }
	};

  $scope.calculateGrandTotal = function(bill) {
  	BillService.setBill(angular.copy(bill));
    $scope.bill = BillService.calculateGrandTotal();
  };

});
<|endoftext|>


<commit_before><!-- GLSL Fragment Shader for Freeciv-web -->
<script id="fragment_shh" type="x-shader/x-fragment">
/**********************************************************************
    Freeciv-web - the web version of Freeciv. http://play.freeciv.org/
    Copyright (C) 2009-2016  The Freeciv-web project

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

***********************************************************************/

#ifdef GL_ES
precision highp float;
#endif

varying vec3 vNormal;

uniform sampler2D lake;
uniform sampler2D coast;
uniform sampler2D floor;
uniform sampler2D arctic;
uniform sampler2D desert;
uniform sampler2D forest;
uniform sampler2D grassland;
uniform sampler2D hills;
uniform sampler2D jungle;
uniform sampler2D mountains;
uniform sampler2D plains;
uniform sampler2D swamp;
uniform sampler2D tundra;
uniform sampler2D beach;

uniform sampler2D maptiles;

uniform int mapWidth;
uniform int mapHeight;

varying vec2 vUv;
varying vec3 vPosition;
varying vec3 vPosition_camera;

float terrain_inaccessible = 0.0;
float terrain_lake = 10.0/255.0;
float terrain_coast = 20.0/255.0;
float terrain_floor = 30.0/255.0;
float terrain_arctic = 40.0/255.0;
float terrain_desert = 50.0/255.0;
float terrain_forest = 60.0/255.0;
float terrain_grassland = 70.0/255.0;
float terrain_hills = 80.0/255.0;
float terrain_jungle = 90.0/255.0;
float terrain_mountains = 100.0/255.0;
float terrain_plains = 110.0/255.0;
float terrain_swamp = 120.0/255.0;
float terrain_tundra = 130.0/255.0;

float heightmap_land = 10.0/255.0;
float heightmap_ocean = 0.0;

void main(void)
{

    vec4 terrain_type = texture2D(maptiles, vec2(vUv.x, vUv.y));
    vec3 c;

    /* Set pixel color based on tile type. */
    if (terrain_type.r + 0.02 > terrain_lake && terrain_type.r - 0.02 < terrain_lake) {
        if (vPosition.y < 53. ) {
          vec4 Cb = texture2D(lake, vec2(vUv.x * 50.0, vUv.y * 50.0));
          c = Cb.rgb;
        } else {
          vec4 Cb = texture2D(grassland, vec2(vUv.x * 50.0, vUv.y * 50.0));
          c = Cb.rgb;
        }
    } else if (terrain_type.r == terrain_coast) {
        if (vPosition.y < 53. ) {
          vec4 Cb = texture2D(coast, vec2(vUv.x * 50.0, vUv.y * 50.0));
          c = Cb.rgb;
        } else {
          vec4 Cb = texture2D(grassland, vec2(vUv.x * 50.0, vUv.y * 50.0));
          c = Cb.rgb;
        }
    } else if (terrain_type.r == terrain_floor) {
        if (vPosition.y < 53. ) {
          vec4 Cb = texture2D(floor, vec2(vUv.x * 50.0, vUv.y * 50.0));
          c = Cb.rgb;
        } else {
          vec4 Cb = texture2D(grassland, vec2(vUv.x * 50.0, vUv.y * 50.0));
          c = Cb.rgb;
        }
    } else if (terrain_type.r == terrain_arctic) {
      vec4 Cb = texture2D(arctic, vec2(vUv.x * 50.0, vUv.y * 50.0));
      c = Cb.rgb;
    } else if (terrain_type.r + 0.02 > terrain_desert && terrain_type.r - 0.02 < terrain_desert) {
      vec4 Cb = texture2D(desert, vec2(vUv.x * 50.0, vUv.y * 50.0));
      c = Cb.rgb;
    } else if (terrain_type.r + 0.02 > terrain_forest && terrain_type.r - 0.02 < terrain_forest) {
      vec4 Cb = texture2D(forest, vec2(vUv.x * 50.0, vUv.y * 50.0));
      c = Cb.rgb;
    } else if (terrain_type.r + 0.02 > terrain_grassland && terrain_type.r - 0.02 < terrain_grassland) {
      vec4 Cb = texture2D(grassland, vec2(vUv.x * 50.0, vUv.y * 50.0));
      c = Cb.rgb;
    } else if (terrain_type.r == terrain_hills) {
      vec4 Cb = texture2D(hills, vec2(vUv.x * 50.0, vUv.y * 50.0));
      c = Cb.rgb;
    } else if (terrain_type.r == terrain_jungle) {
      vec4 Cb = texture2D(jungle, vec2(vUv.x * 50.0, vUv.y * 50.0));
      c = Cb.rgb;
    } else if (terrain_type.r == terrain_mountains) {
      vec4 Cb = texture2D(mountains, vec2(vUv.x * 50.0, vUv.y * 50.0));
      c = Cb.rgb;
    } else if (terrain_type.r + 0.02 > terrain_plains && terrain_type.r - 0.02 < terrain_plains) {
      vec4 Cb = texture2D(plains, vec2(vUv.x * 50.0, vUv.y * 50.0));
      c = Cb.rgb;
    } else if (terrain_type.r == terrain_swamp) {
      vec4 Cb = texture2D(swamp, vec2(vUv.x * 50.0, vUv.y * 50.0));
      c = Cb.rgb;
    } else if (terrain_type.r == terrain_tundra) {
      vec4 Cb = texture2D(tundra, vec2(vUv.x * 50.0, vUv.y * 50.0));
      c = Cb.rgb;
    } else {
      vec4 Cb = texture2D(grassland, vec2(vUv.x * 50.0, vUv.y * 50.0));
      c = Cb.rgb;
    }

  /* render the beach. */
  if (vPosition.y < 54. && vPosition.y > 49.) {
    vec4 Cb = texture2D(beach, vec2(vUv.x * 50.0, vUv.y * 50.0));
    c = Cb.rgb;
  }

  /* specular component, ambient occlusion and fade out underwater terrain */
  float x = clamp((vPosition.y - 30.) / 15., 0., 1.);
  vec4 Cb = texture2D(beach, vec2(vUv.x * 50.0, vUv.y * 50.0));
  c = Cb.rgb * (1. - x) + c * x;

  vec3 light = vec3(0.8, 0.6, 0.7);
  light = normalize(light);

  float dProd = dot(vNormal, light);
  float shade_factor = 0.4 + 0.6 * max(0., dProd);

  vec3 ambiant = vec3(0.27, 0.55, 1.);
  float ambiant_factor = 0.075 * (vPosition_camera.z - 550.) / 400.;
  gl_FragColor.rgb = (1. - ambiant_factor) * c * shade_factor + ambiant * ambiant_factor;

}

</script>

<commit_msg>Beach rendering.<commit_after><!-- GLSL Fragment Shader for Freeciv-web -->
<script id="fragment_shh" type="x-shader/x-fragment">
/**********************************************************************
    Freeciv-web - the web version of Freeciv. http://play.freeciv.org/
    Copyright (C) 2009-2016  The Freeciv-web project

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

***********************************************************************/

#ifdef GL_ES
precision highp float;
#endif

varying vec3 vNormal;

uniform sampler2D lake;
uniform sampler2D coast;
uniform sampler2D floor;
uniform sampler2D arctic;
uniform sampler2D desert;
uniform sampler2D forest;
uniform sampler2D grassland;
uniform sampler2D hills;
uniform sampler2D jungle;
uniform sampler2D mountains;
uniform sampler2D plains;
uniform sampler2D swamp;
uniform sampler2D tundra;
uniform sampler2D beach;

uniform sampler2D maptiles;

uniform int mapWidth;
uniform int mapHeight;

varying vec2 vUv;
varying vec3 vPosition;
varying vec3 vPosition_camera;

float terrain_inaccessible = 0.0;
float terrain_lake = 10.0/255.0;
float terrain_coast = 20.0/255.0;
float terrain_floor = 30.0/255.0;
float terrain_arctic = 40.0/255.0;
float terrain_desert = 50.0/255.0;
float terrain_forest = 60.0/255.0;
float terrain_grassland = 70.0/255.0;
float terrain_hills = 80.0/255.0;
float terrain_jungle = 90.0/255.0;
float terrain_mountains = 100.0/255.0;
float terrain_plains = 110.0/255.0;
float terrain_swamp = 120.0/255.0;
float terrain_tundra = 130.0/255.0;

float heightmap_land = 10.0/255.0;
float heightmap_ocean = 0.0;

float beach_low = 49.0;
float beach_high = 54.0;

void main(void)
{

    vec4 terrain_type = texture2D(maptiles, vec2(vUv.x, vUv.y));
    vec3 c;

    /* Set pixel color based on tile type. */
    if (terrain_type.r + 0.02 > terrain_lake && terrain_type.r - 0.02 < terrain_lake) {
        if (vPosition.y < beach_high ) {
          vec4 Cb = texture2D(lake, vec2(vUv.x * 50.0, vUv.y * 50.0));
          c = Cb.rgb;
        } else {
          vec4 Cb = texture2D(grassland, vec2(vUv.x * 50.0, vUv.y * 50.0));
          c = Cb.rgb;
        }
    } else if (terrain_type.r == terrain_coast) {
        if (vPosition.y < beach_high ) {
          vec4 Cb = texture2D(coast, vec2(vUv.x * 50.0, vUv.y * 50.0));
          c = Cb.rgb;
        } else {
          vec4 Cb = texture2D(grassland, vec2(vUv.x * 50.0, vUv.y * 50.0));
          c = Cb.rgb;
        }
    } else if (terrain_type.r == terrain_floor) {
        if (vPosition.y < beach_high ) {
          vec4 Cb = texture2D(floor, vec2(vUv.x * 50.0, vUv.y * 50.0));
          c = Cb.rgb;
        } else {
          vec4 Cb = texture2D(grassland, vec2(vUv.x * 50.0, vUv.y * 50.0));
          c = Cb.rgb;
        }
    } else if (terrain_type.r == terrain_arctic) {
      vec4 Cb = texture2D(arctic, vec2(vUv.x * 50.0, vUv.y * 50.0));
      c = Cb.rgb;
    } else if (terrain_type.r + 0.02 > terrain_desert && terrain_type.r - 0.02 < terrain_desert) {
      if (vPosition.y > beach_high ) {
        vec4 Cb = texture2D(desert, vec2(vUv.x * 50.0, vUv.y * 50.0));
        c = Cb.rgb;
      } else {
        vec4 Cb = texture2D(coast, vec2(vUv.x * 50.0, vUv.y * 50.0));
        c = Cb.rgb;
      }
    } else if (terrain_type.r + 0.02 > terrain_forest && terrain_type.r - 0.02 < terrain_forest) {
      if (vPosition.y > beach_high ) {
        vec4 Cb = texture2D(forest, vec2(vUv.x * 50.0, vUv.y * 50.0));
        c = Cb.rgb;
      } else {
        vec4 Cb = texture2D(coast, vec2(vUv.x * 50.0, vUv.y * 50.0));
        c = Cb.rgb;
      }
    } else if (terrain_type.r + 0.02 > terrain_grassland && terrain_type.r - 0.02 < terrain_grassland) {
      if (vPosition.y > beach_high ) {
        vec4 Cb = texture2D(grassland, vec2(vUv.x * 50.0, vUv.y * 50.0));
        c = Cb.rgb;
      } else {
        vec4 Cb = texture2D(coast, vec2(vUv.x * 50.0, vUv.y * 50.0));
        c = Cb.rgb;
      }
    } else if (terrain_type.r == terrain_hills) {
      if (vPosition.y > beach_high ) {
        vec4 Cb = texture2D(hills, vec2(vUv.x * 50.0, vUv.y * 50.0));
        c = Cb.rgb;
      } else {
        vec4 Cb = texture2D(coast, vec2(vUv.x * 50.0, vUv.y * 50.0));
        c = Cb.rgb;
      }
    } else if (terrain_type.r == terrain_jungle) {
      if (vPosition.y > beach_high ) {
        vec4 Cb = texture2D(jungle, vec2(vUv.x * 50.0, vUv.y * 50.0));
        c = Cb.rgb;
      } else {
        vec4 Cb = texture2D(coast, vec2(vUv.x * 50.0, vUv.y * 50.0));
        c = Cb.rgb;
      }
    } else if (terrain_type.r == terrain_mountains) {
      if (vPosition.y > beach_high ) {
        vec4 Cb = texture2D(mountains, vec2(vUv.x * 50.0, vUv.y * 50.0));
        c = Cb.rgb;
      } else {
        vec4 Cb = texture2D(coast, vec2(vUv.x * 50.0, vUv.y * 50.0));
        c = Cb.rgb;
      }
    } else if (terrain_type.r + 0.02 > terrain_plains && terrain_type.r - 0.02 < terrain_plains) {
      if (vPosition.y > beach_high ) {
        vec4 Cb = texture2D(plains, vec2(vUv.x * 50.0, vUv.y * 50.0));
        c = Cb.rgb;
      } else {
        vec4 Cb = texture2D(coast, vec2(vUv.x * 50.0, vUv.y * 50.0));
        c = Cb.rgb;
      }
    } else if (terrain_type.r == terrain_swamp) {
      if (vPosition.y > beach_high ) {
        vec4 Cb = texture2D(swamp, vec2(vUv.x * 50.0, vUv.y * 50.0));
        c = Cb.rgb;
      } else {
        vec4 Cb = texture2D(coast, vec2(vUv.x * 50.0, vUv.y * 50.0));
        c = Cb.rgb;
      }
    } else if (terrain_type.r == terrain_tundra) {
      if (vPosition.y > beach_high ) {
        vec4 Cb = texture2D(tundra, vec2(vUv.x * 50.0, vUv.y * 50.0));
        c = Cb.rgb;
      } else {
        vec4 Cb = texture2D(coast, vec2(vUv.x * 50.0, vUv.y * 50.0));
        c = Cb.rgb;
      }
    } else {
      if (vPosition.y > beach_high ) {
        vec4 Cb = texture2D(grassland, vec2(vUv.x * 50.0, vUv.y * 50.0));
        c = Cb.rgb;
      } else {
        vec4 Cb = texture2D(coast, vec2(vUv.x * 50.0, vUv.y * 50.0));
        c = Cb.rgb;
      }
    }

  /* render the beach. */
  if (vPosition.y < beach_high && vPosition.y > beach_low) {
    vec4 Cb = texture2D(beach, vec2(vUv.x * 50.0, vUv.y * 50.0));
    c = Cb.rgb;
  }

  /* specular component, ambient occlusion and fade out underwater terrain */
  float x = clamp((vPosition.y - 30.) / 15., 0., 1.);
  vec4 Cb = texture2D(beach, vec2(vUv.x * 50.0, vUv.y * 50.0));
  c = Cb.rgb * (1. - x) + c * x;

  vec3 light = vec3(0.8, 0.6, 0.7);
  light = normalize(light);

  float dProd = dot(vNormal, light);
  float shade_factor = 0.4 + 0.6 * max(0., dProd);

  vec3 ambiant = vec3(0.27, 0.55, 1.);
  float ambiant_factor = 0.075 * (vPosition_camera.z - 550.) / 400.;
  gl_FragColor.rgb = (1. - ambiant_factor) * c * shade_factor + ambiant * ambiant_factor;

}

</script>

<|endoftext|>