<commit_before>'use strict';

let AppStore = require('./AppStore.js');
let AppMessages = require('./AppMessages.js');

let PubSub = require('pubsub-js');

let AppUtils = {

    scrollTo: function(scrollPos, interval) {

        Velocity(document.body, 'scroll', {
            duration: 250,
            offset: scrollPos,
            easing: 'easeOutQuad',

            // complete: function(elements) {
            //     Velocity(document.body, 'scroll', {
            //         duration: 1,
            //         offset: scrollPos
            //     });
            // }//.bind(this)
        });
    },

    morphElementToHeader: function(fromElement) {

        let newElement =  fromElement.target.cloneNode(true);

        newElement.style.cssText = //'color:#2172F7 !important;' +
            'font-size: 2.03em;color: #737373;cursor: pointer;overflow: hidden;white-space: nowrap;display: block;margin: 0px auto;padding: 20px 50px;text-transform: uppercase;' +
            //document.defaultView.getComputedStyle(document.querySelector('.headerCaption'), '').cssText +
            //document.defaultView.getComputedStyle(fromElement, '').cssText +
                ';position: absolute;top:' + $(fromElement.target).offset().top + 'px;left:' + ($(fromElement.target).offset().left - 100) + 'px';
        document.body.appendChild(newElement);

        fromElement.target.style.cssText = fromElement.target.style.cssText + 'visibility: hidden;';

        Velocity(newElement, {
                'top': '0px',
                'left': '18px',
                'opacity': 0.1
            }, {
                duration: 800,
                easing: 'easeOutQuad',
                //easing: 'easeOutElastic',
                complete: function(elements) {
                    newElement.parentNode.removeChild(newElement);
                }
            }
        );
    },

    markArticleAsRead: function(data) {
        if  (window.readonlyApp) {
            return;
        }

        let markReadData = {}, decodedUrl = decodeURIComponent(data.url);
        markReadData[decodedUrl] = [data.id];

        $.post('/MarkArticlesAsRead?read=1&allRead=-1', {
            'data': JSON.stringify(markReadData)
        }, function(newdata, status, xhr) {
            if (status !== 'success') {
                AppStore.readData[decodedUrl].readCount = AppStore.readData[decodedUrl].readCount - 1;
            }
        });

        if (AppStore.readData[decodedUrl]) {
            AppStore.readData[decodedUrl].readCount = AppStore.readData[decodedUrl].readCount + 1;
        } else {
            AppStore.readData[decodedUrl] = {
                readCount: 1
            };
        }

        if (!AppStore.readData[decodedUrl].localReadData) {
            AppStore.readData[decodedUrl].localReadData = [];
        }

        AppStore.readData[decodedUrl].localReadData.push(data.id);

        this.calcFolderUnreadCount(decodeURIComponent(data.folder));
        PubSub.publish(AppMessages.FOLDERS_UNREAD_COUNT_CHANGED, {});
    },

    starArticle: function(data) {
        $.post('/StarArticle', {
            'data': JSON.stringify(data)
        }, function(newdata, status, xhr) {
            //PubSub.publish('STAR_ARTICLE', JSON.parse(data));
        });
    },

    markFeedAsRead: function(folder, url) {
        let markReadData = {}, decodedUrl = decodeURIComponent(url), decodedFolder = decodeURIComponent(folder);
        markReadData[decodedUrl] = [];

        $.post('/MarkArticlesAsRead?read=1&allRead=1', {
            'data': JSON.stringify(markReadData)
        }, function(data, status, xhr) {
            PubSub.publish(AppMessages.MARK_READ_FEED, JSON.parse(data).feedUrl);
        });

        if (AppStore.readData[decodedUrl]) {
            AppStore.readData[decodedUrl].readCount = AppStore.readData[decodedUrl].totalCount;
        }
        AppStore.nextRequestFromServer[decodedUrl] = true;

        this.calcFolderUnreadCount(decodedFolder);
        PubSub.publish(AppMessages.FOLDERS_UNREAD_COUNT_CHANGED, {});
    },

    markFolderAsRead: function(folder) {
        let feeds = AppStore.userData.bloglist[folder];
        feeds.map(function (feed) {
            this.markFeedAsRead(folder, feed.url);
        }.bind(this));
        this.calcFolderUnreadCount(folder);
        PubSub.publish(AppMessages.FOLDERS_UNREAD_COUNT_CHANGED, {});
    },

    markAllFoldersAsRead: function() {
        Object.keys(AppStore.userData.bloglist).forEach(function(folder) {
            this.markFolderAsRead(folder);
        }.bind(this));
    },

    deleteFolder: function(folder) {
        delete AppStore.userData.bloglist[folder];
        this.saveSettings(function() {
            PubSub.publish(AppMessages.DELETE_FOLDER, folder);
            alert('Folder ' + folder + ' is deleted.');
        });
    },

    unsubscribeFeed: function(folder, feed) {
        function successUnsubscribe() {
            alert('You are now unsubscibed from ' + decodedFeed);
        }

        let decodedFeed = decodeURIComponent(feed);
        let decodedFolder = decodeURIComponent(folder); //todo shouldn't here be encodeURIComponent

        for (let i = 0; i < AppStore.userData.bloglist[decodedFolder].length; i++) {
            if (AppStore.userData.bloglist[decodedFolder][i].url === decodedFeed) {
                AppStore.userData.bloglist[decodedFolder].splice(i, 1);
                this.saveSettingsWithDelete(decodedFeed, successUnsubscribe);
                break;
            }
        }
    },

    updateFeedTitleIfNeeded: function(folder, feed, title) {
        let decodedFeed = decodeURIComponent(feed);
        let decodedFolder = decodeURIComponent(folder); //todo shouldn't here be encodeURIComponent

        if (typeof AppStore.userData.bloglist === 'undefined') {
            return;
        }

        for (let i = 0; i < AppStore.userData.bloglist[decodedFolder].length; i++) {
            if (AppStore.userData.bloglist[decodedFolder][i].url === decodedFeed) {
                if (AppStore.userData.bloglist[decodedFolder][i].title !== title) {
                    AppStore.userData.bloglist[decodedFolder][i].title = title;
                    this.saveSettings();
                }
                break;
            }
        }
    },

    getFeedTitle: function( folder, feed ) {
        let decodedFeed = decodeURIComponent(feed);
        let decodedFolder = decodeURIComponent(folder); //todo shouldn't here be encodeURIComponent

        for (let i = 0; i < AppStore.userData.bloglist[decodedFolder].length; i++) {
            if (AppStore.userData.bloglist[decodedFolder][i].url === decodedFeed) {
                return AppStore.userData.bloglist[decodedFolder][i].title;
            }
        }
    },

    getFeedData: function (feedUrl, successCallback) {
        let colorWheel = document.getElementById('colorWheel');
        let deg = 0;
        let inAnimation = false;
        let animationTimeoutID;
        let rafAnimationID;

        function animateLoader() {
            colorWheel = colorWheel || document.getElementById('colorWheel');
            animationTimeoutID = setTimeout(function() {
                rafAnimationID = requestAnimationFrame(animateLoader);
                deg = deg + 30;
                if (colorWheel) {
                    colorWheel.style.webkitTransform = 'rotate(' +  deg + 'deg)';
                    colorWheel.style.MozTransform = 'rotate(' +  deg + 'deg)';
                    colorWheel.style.msTransform = 'rotate(' +  deg + 'deg)';
                    colorWheel.style.Transform = 'rotate(' +  deg + 'deg)';
                }
            }, 1000 / 30);
        }

        function startAnimation() {
            $('#colorWheel').show();
            if (!inAnimation) {
                inAnimation = true;
                animateLoader();
            }
        }

        function endAnimation () {
            inAnimation = false;
            clearTimeout(animationTimeoutID);
            window.cancelAnimationFrame(rafAnimationID);
            $('#colorWheel').hide();
        }

        startAnimation();

        feedUrl = this.updateForCache(feedUrl);
        $.get('/feed/' + feedUrl, function(result) {
            endAnimation();
            successCallback.apply(this, [result]);
        });
    },

    updateForCache: function(feedUrl) {
        let decodedUrl = decodeURIComponent(feedUrl);

        if (AppStore.nextRequestFromServer[decodedUrl]) {
            AppStore.nextRequestFromServer[decodedUrl] = false;
            feedUrl = feedUrl + '&v=' + Math.random();
        } else {
            let d1 = new Date(); d1.setHours(0); d1.setMinutes(0); d1.setSeconds(0); d1.setMilliseconds(0);
            let d2 = new Date();
            let period =  ((d2 - d1) / 1000 / 60 ) % 60; //waaat :) // ok, get the minutes between two dates (not more than an hour). waat :)
            period =  Math.floor(period / 10);

            feedUrl = feedUrl + '&v=' +  d1.getTime() + '___' + period;
        }
        return feedUrl;
    },

    addNewFeed: function(folder, feed) {

        AppStore.userData.bloglist[folder].push({
            'url': feed,
            'title': feed
        });

        let decodedFeed = decodeURIComponent(feed);

        this.saveSettingsWithAdd(decodedFeed, function() {
            PubSub.publish(AppMessages.NEW_FEED_ADDED, {
                'folder': folder,
                'feed': feed
            });
        });
    },

    addNewFolder: function(folder) {
        if (typeof AppStore.userData.bloglist[folder] === 'undefined') {
            AppStore.userData.bloglist[folder] = [];
            this.saveSettings(function() {
                PubSub.publish(AppMessages.NEW_FOLDER_ADDED, folder);
            });
        }
    },

    saveSettings: function (successCallback) {
        let data = {
            'bloglist': AppStore.userData.bloglist,
            'username': AppStore.userData.username,
            'userSettings': AppStore.userData.userSettings
        };

        $.post('/SaveSettings', {
            'data': JSON.stringify(data)
        }, function () {
            if (typeof successCallback === 'function') {
                successCallback.call();
            }
        });
    },

    saveSettingsWithDelete: function(deleteFeed, successCallback) {
        let data = {
            'bloglist': AppStore.userData.bloglist,
            'username': AppStore.userData.username,
            'userSettings': AppStore.userData.userSettings
        };

        $.post('/SaveSettings?deleteFeed=' + deleteFeed, {
            'data': JSON.stringify(data)
        }, function() {
            if (typeof successCallback === 'function') {
                successCallback.call();
            }
        });
    },

    saveSettingsWithAdd: function(newFeed, successCallback) {
        let data = {
            'bloglist': AppStore.userData.bloglist,
            'username': AppStore.userData.username,
            'userSettings': AppStore.userData.userSettings
        };

        $.post('/SaveSettings?newFeed=' + newFeed, {
            'data': JSON.stringify(data)
        }, function() {
            if (typeof successCallback === 'function') {
                successCallback.call();
            }
        });
    },

    getUserFeeds: function(url, successCallback) {
        $.get(url, function(result) {
            AppStore.userData = result;
            if (typeof successCallback === 'function') {
                successCallback.apply(this, [result]);
            } else {
                return result;
            }

        });
    },

    calcFoldersUnreadCount: function() {
        let folder;
        AppStore.foldersUnreadCount = {};

        for (folder in AppStore.userData.bloglist) {
            this.calcFolderUnreadCount(folder);
        }
    },

    calcFolderUnreadCount: function(folder) {
        let folderUnreadCount = 0, feedUrl;

        if (typeof AppStore.userData.bloglist === 'undefined') {
            return;
        }

        //get all folder feeds
        for (let i = 0; i < AppStore.userData.bloglist[folder].length; i++) {
            feedUrl = AppStore.userData.bloglist[folder][i].url;
            if (typeof AppStore.readData[feedUrl] !== 'undefined') {
                if (AppStore.readData[feedUrl].totalCount - AppStore.readData[feedUrl].readCount > 0) {
                    folderUnreadCount = folderUnreadCount + AppStore.readData[feedUrl].totalCount - AppStore.readData[feedUrl].readCount;
                }
            }
        }

        AppStore.foldersUnreadCount[folder] = folderUnreadCount;

    },

    changeFeedFolder: function(fromFolder, toFolder, url) {
        let decodedUrl = decodeURIComponent(url);
        let decodedFromFolder = decodeURIComponent(fromFolder); //todo shouldn't here be encodeURIComponent
        let decodedToFolder = decodeURIComponent(toFolder); //todo shouldn't here be encodeURIComponent

        //find and remove it
        $.each(AppStore.userData.bloglist[decodedFromFolder], function(i, feed) {
            if (feed.url === decodedUrl) {
                AppStore.userData.bloglist[decodedFromFolder].splice(i, 1);
                AppStore.userData.bloglist[decodedToFolder].push(feed);
                return false;
            }
        });

        this.saveSettings(function() {
            PubSub.publish(AppMessages.FEED_FOLDER_CHANGED, {
                'toFolder': toFolder,
                'feed': url
            });
        });

        this.calcFolderUnreadCount(decodedFromFolder);
        this.calcFolderUnreadCount(decodedToFolder);
    },

    getReadData: function(url, initialFolder ) {
        $.get(url, function(data) {
            AppStore.readData = data;
            this.calcFoldersUnreadCount();
            PubSub.publish(AppMessages.FOLDERS_UNREAD_COUNT_CHANGED, {});
            PubSub.publish(AppMessages.FEED_READ_COUNT_CHANGED, {
                folder: initialFolder
            });
        }.bind(this));
    },

    generateUploadUrlHandler: function(successCallback) {
        $.get('/GenerateUploadUrl', function(data) {
            successCallback.apply(this, [data]);
        });
    }
};

module.exports = AppUtils;
<commit_msg>remove jquery calls<commit_after>'use strict';

let AppStore = require('./AppStore.js');
let AppMessages = require('./AppMessages.js');

let PubSub = require('pubsub-js');

let AppUtils = {

    scrollTo: function(scrollPos, interval) {
        Velocity(document.body, 'scroll', {
            duration: 250,
            offset: scrollPos,
            easing: 'easeOutQuad',
        });
    },

    morphElementToHeader: function(fromElement) {

        let newElement =  fromElement.target.cloneNode(true);

        newElement.style.cssText = //'color:#2172F7 !important;' +
            'font-size: 2.03em;color: #737373;cursor: pointer;overflow: hidden;white-space: nowrap;display: block;margin: 0px auto;padding: 20px 50px;text-transform: uppercase;' +
            //document.defaultView.getComputedStyle(document.querySelector('.headerCaption'), '').cssText +
            //document.defaultView.getComputedStyle(fromElement, '').cssText +
                ';position: absolute;top:' + fromElement.target.offsetTop + 'px;left:' + (fromElement.target.offsetLeft - 100) + 'px';
        document.body.appendChild(newElement);

        fromElement.target.style.cssText = fromElement.target.style.cssText + 'visibility: hidden;';

        Velocity(newElement, {
                'top': '0px',
                'left': '18px',
                'opacity': 0.1
            }, {
                duration: 800,
                easing: 'easeOutQuad',
                //easing: 'easeOutElastic',
                complete: function(elements) {
                    newElement.parentNode.removeChild(newElement);
                }
            }
        );
    },

    markArticleAsRead: function(data) {
        if  (window.readonlyApp) {
            return;
        }

        let markReadData = {}, decodedUrl = decodeURIComponent(data.url);
        markReadData[decodedUrl] = [data.id];

        $.post('/MarkArticlesAsRead?read=1&allRead=-1', {
            'data': JSON.stringify(markReadData)
        }, function(newdata, status, xhr) {
            if (status !== 'success') {
                AppStore.readData[decodedUrl].readCount = AppStore.readData[decodedUrl].readCount - 1;
            }
        });

        if (AppStore.readData[decodedUrl]) {
            AppStore.readData[decodedUrl].readCount = AppStore.readData[decodedUrl].readCount + 1;
        } else {
            AppStore.readData[decodedUrl] = {
                readCount: 1
            };
        }

        if (!AppStore.readData[decodedUrl].localReadData) {
            AppStore.readData[decodedUrl].localReadData = [];
        }

        AppStore.readData[decodedUrl].localReadData.push(data.id);

        this.calcFolderUnreadCount(decodeURIComponent(data.folder));
        PubSub.publish(AppMessages.FOLDERS_UNREAD_COUNT_CHANGED, {});
    },

    starArticle: function(data) {
        $.post('/StarArticle', {
            'data': JSON.stringify(data)
        }, function(newdata, status, xhr) {
            //PubSub.publish('STAR_ARTICLE', JSON.parse(data));
        });
    },

    markFeedAsRead: function(folder, url) {
        let markReadData = {}, decodedUrl = decodeURIComponent(url), decodedFolder = decodeURIComponent(folder);
        markReadData[decodedUrl] = [];

        $.post('/MarkArticlesAsRead?read=1&allRead=1', {
            'data': JSON.stringify(markReadData)
        }, function(data, status, xhr) {
            PubSub.publish(AppMessages.MARK_READ_FEED, JSON.parse(data).feedUrl);
        });

        if (AppStore.readData[decodedUrl]) {
            AppStore.readData[decodedUrl].readCount = AppStore.readData[decodedUrl].totalCount;
        }
        AppStore.nextRequestFromServer[decodedUrl] = true;

        this.calcFolderUnreadCount(decodedFolder);
        PubSub.publish(AppMessages.FOLDERS_UNREAD_COUNT_CHANGED, {});
    },

    markFolderAsRead: function(folder) {
        let feeds = AppStore.userData.bloglist[folder];
        feeds.map(function (feed) {
            this.markFeedAsRead(folder, feed.url);
        }.bind(this));
        this.calcFolderUnreadCount(folder);
        PubSub.publish(AppMessages.FOLDERS_UNREAD_COUNT_CHANGED, {});
    },

    markAllFoldersAsRead: function() {
        Object.keys(AppStore.userData.bloglist).forEach(function(folder) {
            this.markFolderAsRead(folder);
        }.bind(this));
    },

    deleteFolder: function(folder) {
        delete AppStore.userData.bloglist[folder];
        this.saveSettings(function() {
            PubSub.publish(AppMessages.DELETE_FOLDER, folder);
            alert('Folder ' + folder + ' is deleted.');
        });
    },

    unsubscribeFeed: function(folder, feed) {
        function successUnsubscribe() {
            alert('You are now unsubscibed from ' + decodedFeed);
        }

        let decodedFeed = decodeURIComponent(feed);
        let decodedFolder = decodeURIComponent(folder); //todo shouldn't here be encodeURIComponent

        for (let i = 0; i < AppStore.userData.bloglist[decodedFolder].length; i++) {
            if (AppStore.userData.bloglist[decodedFolder][i].url === decodedFeed) {
                AppStore.userData.bloglist[decodedFolder].splice(i, 1);
                this.saveSettingsWithDelete(decodedFeed, successUnsubscribe);
                break;
            }
        }
    },

    updateFeedTitleIfNeeded: function(folder, feed, title) {
        let decodedFeed = decodeURIComponent(feed);
        let decodedFolder = decodeURIComponent(folder); //todo shouldn't here be encodeURIComponent

        if (typeof AppStore.userData.bloglist === 'undefined') {
            return;
        }

        for (let i = 0; i < AppStore.userData.bloglist[decodedFolder].length; i++) {
            if (AppStore.userData.bloglist[decodedFolder][i].url === decodedFeed) {
                if (AppStore.userData.bloglist[decodedFolder][i].title !== title) {
                    AppStore.userData.bloglist[decodedFolder][i].title = title;
                    this.saveSettings();
                }
                break;
            }
        }
    },

    getFeedTitle: function( folder, feed ) {
        let decodedFeed = decodeURIComponent(feed);
        let decodedFolder = decodeURIComponent(folder); //todo shouldn't here be encodeURIComponent

        for (let i = 0; i < AppStore.userData.bloglist[decodedFolder].length; i++) {
            if (AppStore.userData.bloglist[decodedFolder][i].url === decodedFeed) {
                return AppStore.userData.bloglist[decodedFolder][i].title;
            }
        }
    },

    getFeedData: function (feedUrl, successCallback) {
        let colorWheel = document.getElementById('colorWheel');
        let deg = 0;
        let inAnimation = false;
        let animationTimeoutID;
        let rafAnimationID;

        function animateLoader() {
            colorWheel = colorWheel || document.getElementById('colorWheel')
            animationTimeoutID = setTimeout(function() {
                rafAnimationID = requestAnimationFrame(animateLoader);
                deg = deg + 30;
                if (colorWheel) {
                    colorWheel.style.webkitTransform = 'rotate(' +  deg + 'deg)';
                    colorWheel.style.MozTransform = 'rotate(' +  deg + 'deg)';
                    colorWheel.style.msTransform = 'rotate(' +  deg + 'deg)';
                    colorWheel.style.Transform = 'rotate(' +  deg + 'deg)';
                }
            }, 1000 / 30);
        }

        function startAnimation() {
            colorWheel.classList.remove('displayNone');
            if (!inAnimation) {
                inAnimation = true;
                animateLoader();
            }
        }

        function endAnimation () {
            inAnimation = false;
            clearTimeout(animationTimeoutID);
            window.cancelAnimationFrame(rafAnimationID);
            colorWheel.classList.add('displayNone');
        }

        startAnimation();

        feedUrl = this.updateForCache(feedUrl);
        $.get('/feed/' + feedUrl, function(result) {
            endAnimation();
            successCallback.apply(this, [result]);
        });
    },

    updateForCache: function(feedUrl) {
        let decodedUrl = decodeURIComponent(feedUrl);

        if (AppStore.nextRequestFromServer[decodedUrl]) {
            AppStore.nextRequestFromServer[decodedUrl] = false;
            feedUrl = feedUrl + '&v=' + Math.random();
        } else {
            let d1 = new Date(); d1.setHours(0); d1.setMinutes(0); d1.setSeconds(0); d1.setMilliseconds(0);
            let d2 = new Date();
            let period =  ((d2 - d1) / 1000 / 60 ) % 60; //waaat :) // ok, get the minutes between two dates (not more than an hour). waat :)
            period =  Math.floor(period / 10);

            feedUrl = feedUrl + '&v=' +  d1.getTime() + '___' + period;
        }
        return feedUrl;
    },

    addNewFeed: function(folder, feed) {

        AppStore.userData.bloglist[folder].push({
            'url': feed,
            'title': feed
        });

        let decodedFeed = decodeURIComponent(feed);

        this.saveSettingsWithAdd(decodedFeed, function() {
            PubSub.publish(AppMessages.NEW_FEED_ADDED, {
                'folder': folder,
                'feed': feed
            });
        });
    },

    addNewFolder: function(folder) {
        if (typeof AppStore.userData.bloglist[folder] === 'undefined') {
            AppStore.userData.bloglist[folder] = [];
            this.saveSettings(function() {
                PubSub.publish(AppMessages.NEW_FOLDER_ADDED, folder);
            });
        }
    },

    saveSettings: function (successCallback) {
        let data = {
            'bloglist': AppStore.userData.bloglist,
            'username': AppStore.userData.username,
            'userSettings': AppStore.userData.userSettings
        };

        $.post('/SaveSettings', {
            'data': JSON.stringify(data)
        }, function () {
            if (typeof successCallback === 'function') {
                successCallback.call();
            }
        });
    },

    saveSettingsWithDelete: function(deleteFeed, successCallback) {
        let data = {
            'bloglist': AppStore.userData.bloglist,
            'username': AppStore.userData.username,
            'userSettings': AppStore.userData.userSettings
        };

        $.post('/SaveSettings?deleteFeed=' + deleteFeed, {
            'data': JSON.stringify(data)
        }, function() {
            if (typeof successCallback === 'function') {
                successCallback.call();
            }
        });
    },

    saveSettingsWithAdd: function(newFeed, successCallback) {
        let data = {
            'bloglist': AppStore.userData.bloglist,
            'username': AppStore.userData.username,
            'userSettings': AppStore.userData.userSettings
        };

        $.post('/SaveSettings?newFeed=' + newFeed, {
            'data': JSON.stringify(data)
        }, function() {
            if (typeof successCallback === 'function') {
                successCallback.call();
            }
        });
    },

    getUserFeeds: function(url, successCallback) {
        $.get(url, function(result) {
            AppStore.userData = result;
            if (typeof successCallback === 'function') {
                successCallback.apply(this, [result]);
            } else {
                return result;
            }

        });
    },

    calcFoldersUnreadCount: function() {
        let folder;
        AppStore.foldersUnreadCount = {};

        for (folder in AppStore.userData.bloglist) {
            this.calcFolderUnreadCount(folder);
        }
    },

    calcFolderUnreadCount: function(folder) {
        let folderUnreadCount = 0, feedUrl;

        if (typeof AppStore.userData.bloglist === 'undefined') {
            return;
        }

        //get all folder feeds
        for (let i = 0; i < AppStore.userData.bloglist[folder].length; i++) {
            feedUrl = AppStore.userData.bloglist[folder][i].url;
            if (typeof AppStore.readData[feedUrl] !== 'undefined') {
                if (AppStore.readData[feedUrl].totalCount - AppStore.readData[feedUrl].readCount > 0) {
                    folderUnreadCount = folderUnreadCount + AppStore.readData[feedUrl].totalCount - AppStore.readData[feedUrl].readCount;
                }
            }
        }

        AppStore.foldersUnreadCount[folder] = folderUnreadCount;

    },

    changeFeedFolder: function(fromFolder, toFolder, url) {
        let decodedUrl = decodeURIComponent(url);
        let decodedFromFolder = decodeURIComponent(fromFolder); //todo shouldn't here be encodeURIComponent
        let decodedToFolder = decodeURIComponent(toFolder); //todo shouldn't here be encodeURIComponent

        //find and remove it
        $.each(AppStore.userData.bloglist[decodedFromFolder], function(i, feed) {
            if (feed.url === decodedUrl) {
                AppStore.userData.bloglist[decodedFromFolder].splice(i, 1);
                AppStore.userData.bloglist[decodedToFolder].push(feed);
                return false;
            }
        });

        this.saveSettings(function() {
            PubSub.publish(AppMessages.FEED_FOLDER_CHANGED, {
                'toFolder': toFolder,
                'feed': url
            });
        });

        this.calcFolderUnreadCount(decodedFromFolder);
        this.calcFolderUnreadCount(decodedToFolder);
    },

    getReadData: function(url, initialFolder ) {
        $.get(url, function(data) {
            AppStore.readData = data;
            this.calcFoldersUnreadCount();
            PubSub.publish(AppMessages.FOLDERS_UNREAD_COUNT_CHANGED, {});
            PubSub.publish(AppMessages.FEED_READ_COUNT_CHANGED, {
                folder: initialFolder
            });
        }.bind(this));
    },

    generateUploadUrlHandler: function(successCallback) {
        $.get('/GenerateUploadUrl', function(data) {
            successCallback.apply(this, [data]);
        });
    }
};

module.exports = AppUtils;
<|endoftext|>


<commit_before>'use strict';

var Joi = require('joi');
var _ = require('lodash');
var vm = require('vm');
var util = require('../util/util');
var async = require('async');
var debug = require('debug')('formio:validator');

/**
 * @TODO: Add description.
 *
 * @param form
 * @param model
 * @constructor
 */
var Validator = function(form, model) {
  this.customValidations = {};
  this.schema = null;
  this.model = model;
  this.ignore = {};
  this.unique = {};
  this.form = form;
};

/**
 * Returns a validator per component.
 */
Validator.prototype.getValidator = function(component) {
  var fieldValidator = null;
  if (!component) {
    return;
  }

  // If the value must be unique.
  if (component.unique) {
    this.unique[component.key] = component;
  }

  // The value is persistent if it doesn't say otherwise or explicitly says so.
  var isPersistent = !component.hasOwnProperty('persistent') || component.persistent;

  // Add the custom validations.
  if (component.validate && component.validate.custom && isPersistent) {
    this.customValidations[component.key] = component;
  }

  /* eslint-disable max-depth, valid-typeof */
  var objectSchema = {};
  switch (component.type) {
    case 'datagrid':
      util.eachComponent(component.components, function(dgridComp) {
        objectSchema[dgridComp.key] = this.getValidator(dgridComp);
      }.bind(this));
      fieldValidator = Joi.array().items(Joi.object(objectSchema));
      break;
    case 'container':
      util.eachComponent(component.components, function(dgridComp) {
        objectSchema[dgridComp.key] = this.getValidator(dgridComp);
      }.bind(this));
      fieldValidator = Joi.object(objectSchema);
      break;
    case 'textfield':
    case 'textarea':
    case 'phonenumber':
      fieldValidator = Joi.string().empty('');
      if (
        component.validate &&
        component.validate.hasOwnProperty('minLength') &&
        (typeof component.validate.minLength === 'number') &&
        component.validate.minLength >= 0
      ) {
        fieldValidator = fieldValidator.min(component.validate.minLength);
      }
      if (
        component.validate &&
        component.validate.hasOwnProperty('maxLength') &&
        (typeof component.validate.maxLength === 'number') &&
        component.validate.maxLength >= 0
      ) {
        fieldValidator = fieldValidator.max(component.validate.maxLength);
      }
      break;
    case 'email':
      fieldValidator = Joi.string().email().empty('');
      break;
    case 'number':
      fieldValidator = Joi.number().empty(null);
      if (component.validate) {
        // If the step is provided... we can infer float vs. integer.
        if (component.validate.step && (typeof component.validate.step !== 'any')) {
          var parts = component.validate.step.split('.');
          if (parts.length === 1) {
            fieldValidator = fieldValidator.integer();
          }
          else {
            fieldValidator = fieldValidator.precision(parts[1].length);
          }
        }

        _.each(['min', 'max', 'greater', 'less'], function(check) {
          if (component.validate[check] && (typeof component.validate[check] === 'number')) {
            fieldValidator = fieldValidator[check](component.validate[check]);
          }
        });
      }
      break;
    default:
      fieldValidator = Joi.any();
      break;
  }
  /* eslint-enable max-depth, valid-typeof */

  // Only run validations for persistent fields with values but not on embedded.
  if (component.key && (component.key.indexOf('.') === -1) && isPersistent && component.validate) {
    // Add required validator.
    if (component.validate.required) {
      fieldValidator = fieldValidator.required().empty();
    }

    // Add regex validator
    if (component.validate.pattern) {
      var regex = new RegExp(component.validate.pattern);
      fieldValidator = fieldValidator.regex(regex);
    }
  }

  // Make sure to change this to an array if multiple is checked.
  if (component.multiple) {
    fieldValidator = Joi.array().sparse().items(fieldValidator);
  }

  return fieldValidator;
};

/**
 * Using the submission, determine which fields need to be validated and ignored.
 *
 * @param {Object} submission
 *   The data submission object.
 */
Validator.prototype.buildIgnoreList = function(submission) {
  var boolean = {
    'true': true,
    'false': false
  };
  var show = {};
  util.eachComponent(this.form.components, function(component) {
    // By default, each field is shown.
    show[component.key] = true;

    // Only change display options if all required conditional properties are present.
    if (
      component.conditional
      && (component.conditional.show !== null && component.conditional.show !== '')
      && (component.conditional.when !== null && component.conditional.when !== '')
    ) {
      // Default the conditional values.
      component.conditional.show = boolean[component.conditional.show];
      component.conditional.eq = component.conditional.eq || '';

      // Get the conditional component.
      var cond = util.getComponent(this.form.components, component.conditional.when.toString());
      if (!cond) {
        return;
      }
      var value = submission.data[cond.key];

      if (value && typeof value !== 'object') {
        // Check if the conditional value is equal to the trigger value
        show[component.key] = value.toString() === component.conditional.eq.toString()
          ? boolean[component.conditional.show]
          : !boolean[component.conditional.show];
      }
      // Special check for check boxes component.
      else if (value && typeof value === 'object') {
        // Check if the conditional trigger value is true.
        show[component.key] = boolean[value[component.conditional.eq].toString()];
      }
      // Check against the components default value, if present and the components hasnt been interacted with.
      else if (!value && cond.defaultValue) {
        show[component.key] = cond.defaultValue.toString() === component.conditional.eq.toString()
          ? boolean[component.conditional.show]
          : !boolean[component.conditional.show];
      }
      // If there is no value, we still need to process as not equal.
      else {
        show[component.key] = !boolean[component.conditional.show];
      }
    }
  }.bind(this));

  // Iterate each component were supposed to show, if we find one we're not supposed to show, add it to the ignore.
  _.each(show, function(value, key) {
    try {
      // If this component isn't being displayed, dont require it.
      if (!boolean[value]) {
        this.ignore[key] = true;
      }
    }
    catch (e) {
      debug(e);
    }
  }.bind(this));
};

/**
 * Using the form, ignore list and unique list, build the joi schema for validation.
 */
Validator.prototype.buildSchema = function() {
  // Flatten the components array.
  var components = util.flattenComponents(this.form.components);

  // Remove all keys within a data grid.
  _.each(components, function(component) {
    if (component.type === 'datagrid' || component.type === 'container') {
      util.eachComponent(component.components, function(dgridComp) {
        this.ignore[dgridComp.key] = true;
      }.bind(this));
    }
  }.bind(this));

  // Build the Joi validation schema.
  var keys = {
    // Start off with the _id key.
    _id: Joi.string().meta({primaryKey: true})
  };

  // Iterate through each component.
  _.each(components, function(component) {
    if (!component) {
      return;
    }

    // See if we should ignore validation for this component.
    if (this.ignore.hasOwnProperty(component.key)) {
      return;
    }

    // Get the validator.
    var fieldValidator = this.getValidator(component);

    // Add the validator.
    if (fieldValidator) {
      keys[component.key] = fieldValidator;
    }
  }.bind(this));

  // Create the validator schema.
  this.schema = Joi.object().keys(keys);
};

/**
 * Validate a submission for a form.
 *
 * @param submission
 * @param next
 * @returns {*}
 */
Validator.prototype.validate = function(submission, next) {
  var valid = true;
  var error = null;
  debug('Starting validation');

  // Skip validation if no data is provided.
  if (!submission.data) {
    debug('No data skipping validation');
    return next();
  }

  // Using the submission, determine which fields are supposed to be shown; Only validate displayed fields.
  this.buildIgnoreList(submission);

  // Build the validator schema.
  this.buildSchema();

  /**
   * Invoke the Joi validator with our data.
   *
   * @type {function(this:Validator)}
   */
  var joiValidate = function() {
    Joi.validate(submission.data, this.schema, {stripUnknown: true}, function(validateErr, value) {
      if (validateErr) {
        debug(validateErr);
        return next(validateErr);
      }

      next(null, value);
    });
  }.bind(this);

  // Check for custom validations.
  for (var key in this.customValidations) {
    // If there is a value for this submission....
    if (this.customValidations.hasOwnProperty(key) && (submission.data[key] !== undefined)) {
      var component = this.customValidations[key];

      // Try a new sandboxed validation.
      try {
        // Replace with variable substitutions.
        component.validate.custom = component.validate.custom.replace(/({{\s+(.*)\s+}})/, function(match, $1, $2) {
          return submission.data[$2];
        });

        // Create the sandbox.
        var sandbox = vm.createContext({
          input: submission.data[key],
          component: component,
          valid: valid
        });

        // Execute the script.
        var script = new vm.Script(component.validate.custom);
        script.runInContext(sandbox, {
          timeout: 100
        });
        valid = sandbox.valid;
      }
      catch (err) {
        // Say this isn't valid based on bad code executed...
        valid = err.toString();
      }

      // If there is an error, then set the error object and break from iterations.
      if (valid !== true) {
        error = {
          message: valid,
          path: key,
          type: component.type + '.custom'
        };
        break;
      }
    }
  }

  // If an error has occured in custom validation, fail immediately.
  if (error) {
    var temp = {name: 'ValidationError', details: [error]};
    debug(error);
    return next(temp);
  }

  // Iterate through each of the unique keys.
  var uniques = _.keys(this.unique);
  if (uniques.length > 0) {
    async.eachSeries(uniques, function(key, done) {
      var component = this.unique[key];

      // Unique fields must always be set with a value.
      debug('Key: ' + key);
      if (
        !submission.data.hasOwnProperty(key) &&
        !submission.data[key]
      ) {
        // Throw an error if this isn't a resource field.
        if (key.indexOf('.') === -1) {
          debug('Unique field: ' + key + ', was not a resource field.');
          return done(new Error('Unique fields cannot be empty.'));
        }
        else {
          return done();
        }
      }

      // Get the query.
      var query = {form: util.idToBson(submission.form)};
      query['data.' + key] = submission.data[key];

      // Only search for non-deleted items.
      if (!query.hasOwnProperty('deleted')) {
        query['deleted'] = {$eq: null};
      }

      // Try to find an existing value within the form.
      debug(query);
      this.model.findOne(query, function(err, result) {
        if (err) {
          debug(err);
          return done(err);
        }
        if (result && submission._id && (result._id.toString() === submission._id)) {
          return done();
        }
        if (result) {
          return done(new Error(component.label + ' must be unique.'));
        }

        done();
      });
    }.bind(this), function(err) {
      if (err) {
        return next(err.message);
      }

      joiValidate();
    });
  }
  else {
    joiValidate();
  }
};

module.exports = Validator;
<commit_msg>Adding custom conditional logic validation on the backend.<commit_after>'use strict';

var Joi = require('joi');
var _ = require('lodash');
var vm = require('vm');
var util = require('../util/util');
var async = require('async');
var debug = require('debug')('formio:validator');

/**
 * @TODO: Add description.
 *
 * @param form
 * @param model
 * @constructor
 */
var Validator = function(form, model) {
  this.customValidations = {};
  this.schema = null;
  this.model = model;
  this.ignore = {};
  this.unique = {};
  this.form = form;
};

/**
 * Returns a validator per component.
 */
Validator.prototype.getValidator = function(component) {
  var fieldValidator = null;
  if (!component) {
    return;
  }

  // If the value must be unique.
  if (component.unique) {
    this.unique[component.key] = component;
  }

  // The value is persistent if it doesn't say otherwise or explicitly says so.
  var isPersistent = !component.hasOwnProperty('persistent') || component.persistent;

  // Add the custom validations.
  if (component.validate && component.validate.custom && isPersistent) {
    this.customValidations[component.key] = component;
  }

  /* eslint-disable max-depth, valid-typeof */
  var objectSchema = {};
  switch (component.type) {
    case 'datagrid':
      util.eachComponent(component.components, function(dgridComp) {
        objectSchema[dgridComp.key] = this.getValidator(dgridComp);
      }.bind(this));
      fieldValidator = Joi.array().items(Joi.object(objectSchema));
      break;
    case 'container':
      util.eachComponent(component.components, function(dgridComp) {
        objectSchema[dgridComp.key] = this.getValidator(dgridComp);
      }.bind(this));
      fieldValidator = Joi.object(objectSchema);
      break;
    case 'textfield':
    case 'textarea':
    case 'phonenumber':
      fieldValidator = Joi.string().empty('');
      if (
        component.validate &&
        component.validate.hasOwnProperty('minLength') &&
        (typeof component.validate.minLength === 'number') &&
        component.validate.minLength >= 0
      ) {
        fieldValidator = fieldValidator.min(component.validate.minLength);
      }
      if (
        component.validate &&
        component.validate.hasOwnProperty('maxLength') &&
        (typeof component.validate.maxLength === 'number') &&
        component.validate.maxLength >= 0
      ) {
        fieldValidator = fieldValidator.max(component.validate.maxLength);
      }
      break;
    case 'email':
      fieldValidator = Joi.string().email().empty('');
      break;
    case 'number':
      fieldValidator = Joi.number().empty(null);
      if (component.validate) {
        // If the step is provided... we can infer float vs. integer.
        if (component.validate.step && (typeof component.validate.step !== 'any')) {
          var parts = component.validate.step.split('.');
          if (parts.length === 1) {
            fieldValidator = fieldValidator.integer();
          }
          else {
            fieldValidator = fieldValidator.precision(parts[1].length);
          }
        }

        _.each(['min', 'max', 'greater', 'less'], function(check) {
          if (component.validate[check] && (typeof component.validate[check] === 'number')) {
            fieldValidator = fieldValidator[check](component.validate[check]);
          }
        });
      }
      break;
    default:
      fieldValidator = Joi.any();
      break;
  }
  /* eslint-enable max-depth, valid-typeof */

  // Only run validations for persistent fields with values but not on embedded.
  if (component.key && (component.key.indexOf('.') === -1) && isPersistent && component.validate) {
    // Add required validator.
    if (component.validate.required) {
      fieldValidator = fieldValidator.required().empty();
    }

    // Add regex validator
    if (component.validate.pattern) {
      var regex = new RegExp(component.validate.pattern);
      fieldValidator = fieldValidator.regex(regex);
    }
  }

  // Make sure to change this to an array if multiple is checked.
  if (component.multiple) {
    fieldValidator = Joi.array().sparse().items(fieldValidator);
  }

  return fieldValidator;
};

/**
 * Using the submission, determine which fields need to be validated and ignored.
 *
 * @param {Object} submission
 *   The data submission object.
 */
Validator.prototype.buildIgnoreList = function(submission) {
  var boolean = {
    'true': true,
    'false': false
  };
  var show = {};
  util.eachComponent(this.form.components, function(component) {
    // By default, each field is shown.
    show[component.key] = true;

    // Only change display options if all required conditional properties are present.
    if (
      component.conditional
      && (component.conditional.show !== null && component.conditional.show !== '')
      && (component.conditional.when !== null && component.conditional.when !== '')
    ) {
      // Default the conditional values.
      component.conditional.show = boolean[component.conditional.show];
      component.conditional.eq = component.conditional.eq || '';

      // Get the conditional component.
      var cond = util.getComponent(this.form.components, component.conditional.when.toString());
      if (!cond) {
        return;
      }
      var value = submission.data[cond.key];

      if (value && typeof value !== 'object') {
        // Check if the conditional value is equal to the trigger value
        show[component.key] = value.toString() === component.conditional.eq.toString()
          ? boolean[component.conditional.show]
          : !boolean[component.conditional.show];
      }
      // Special check for check boxes component.
      else if (value && typeof value === 'object') {
        // Check if the conditional trigger value is true.
        show[component.key] = boolean[value[component.conditional.eq].toString()];
      }
      // Check against the components default value, if present and the components hasnt been interacted with.
      else if (!value && cond.defaultValue) {
        show[component.key] = cond.defaultValue.toString() === component.conditional.eq.toString()
          ? boolean[component.conditional.show]
          : !boolean[component.conditional.show];
      }
      // If there is no value, we still need to process as not equal.
      else {
        show[component.key] = !boolean[component.conditional.show];
      }
    }
    // Custom conditional logic.
    else if (component.customConditional) {
      try {
        // Create the sandbox.
        var sandbox = vm.createContext({
          data: submission.data
        });

        // Execute the script.
        var script = new vm.Script(component.customConditional);
        script.runInContext(sandbox, {
          timeout: 250
        });

        if (boolean.hasOwnProperty(sandbox.show)) {
          show[component.key] = boolean[sandbox.show];
        }
        else {
          show[component.key] = true;
        }
      }
      catch (e) {
        debug('Custom Conditional Error: ');
        debug(e);
        // Default to true, if a validation error occurred.
        show[component.key] = true;
      }
    }
  }.bind(this));

  // Iterate each component were supposed to show, if we find one we're not supposed to show, add it to the ignore.
  _.each(show, function(value, key) {
    try {
      // If this component isn't being displayed, dont require it.
      if (!boolean[value]) {
        this.ignore[key] = true;
      }
    }
    catch (e) {
      debug(e);
    }
  }.bind(this));
};

/**
 * Using the form, ignore list and unique list, build the joi schema for validation.
 */
Validator.prototype.buildSchema = function() {
  // Flatten the components array.
  var components = util.flattenComponents(this.form.components);

  // Remove all keys within a data grid.
  _.each(components, function(component) {
    if (component.type === 'datagrid' || component.type === 'container') {
      util.eachComponent(component.components, function(dgridComp) {
        this.ignore[dgridComp.key] = true;
      }.bind(this));
    }
  }.bind(this));

  // Build the Joi validation schema.
  var keys = {
    // Start off with the _id key.
    _id: Joi.string().meta({primaryKey: true})
  };

  // Iterate through each component.
  _.each(components, function(component) {
    if (!component) {
      return;
    }

    // See if we should ignore validation for this component.
    if (this.ignore.hasOwnProperty(component.key)) {
      return;
    }

    // Get the validator.
    var fieldValidator = this.getValidator(component);

    // Add the validator.
    if (fieldValidator) {
      keys[component.key] = fieldValidator;
    }
  }.bind(this));

  // Create the validator schema.
  this.schema = Joi.object().keys(keys);
};

/**
 * Validate a submission for a form.
 *
 * @param submission
 * @param next
 * @returns {*}
 */
Validator.prototype.validate = function(submission, next) {
  var valid = true;
  var error = null;
  debug('Starting validation');

  // Skip validation if no data is provided.
  if (!submission.data) {
    debug('No data skipping validation');
    return next();
  }

  // Using the submission, determine which fields are supposed to be shown; Only validate displayed fields.
  this.buildIgnoreList(submission);

  // Build the validator schema.
  this.buildSchema();

  /**
   * Invoke the Joi validator with our data.
   *
   * @type {function(this:Validator)}
   */
  var joiValidate = function() {
    Joi.validate(submission.data, this.schema, {stripUnknown: true}, function(validateErr, value) {
      if (validateErr) {
        debug(validateErr);
        return next(validateErr);
      }

      next(null, value);
    });
  }.bind(this);

  // Check for custom validations.
  for (var key in this.customValidations) {
    // If there is a value for this submission....
    if (this.customValidations.hasOwnProperty(key) && (submission.data[key] !== undefined)) {
      var component = this.customValidations[key];

      // Try a new sandboxed validation.
      try {
        // Replace with variable substitutions.
        component.validate.custom = component.validate.custom.replace(/({{\s+(.*)\s+}})/, function(match, $1, $2) {
          return submission.data[$2];
        });

        // Create the sandbox.
        var sandbox = vm.createContext({
          input: submission.data[key],
          component: component,
          valid: valid
        });

        // Execute the script.
        var script = new vm.Script(component.validate.custom);
        script.runInContext(sandbox, {
          timeout: 100
        });
        valid = sandbox.valid;
      }
      catch (err) {
        // Say this isn't valid based on bad code executed...
        valid = err.toString();
      }

      // If there is an error, then set the error object and break from iterations.
      if (valid !== true) {
        error = {
          message: valid,
          path: key,
          type: component.type + '.custom'
        };
        break;
      }
    }
  }

  // If an error has occured in custom validation, fail immediately.
  if (error) {
    var temp = {name: 'ValidationError', details: [error]};
    debug(error);
    return next(temp);
  }

  // Iterate through each of the unique keys.
  var uniques = _.keys(this.unique);
  if (uniques.length > 0) {
    async.eachSeries(uniques, function(key, done) {
      var component = this.unique[key];

      // Unique fields must always be set with a value.
      debug('Key: ' + key);
      if (
        !submission.data.hasOwnProperty(key) &&
        !submission.data[key]
      ) {
        // Throw an error if this isn't a resource field.
        if (key.indexOf('.') === -1) {
          debug('Unique field: ' + key + ', was not a resource field.');
          return done(new Error('Unique fields cannot be empty.'));
        }
        else {
          return done();
        }
      }

      // Get the query.
      var query = {form: util.idToBson(submission.form)};
      query['data.' + key] = submission.data[key];

      // Only search for non-deleted items.
      if (!query.hasOwnProperty('deleted')) {
        query['deleted'] = {$eq: null};
      }

      // Try to find an existing value within the form.
      debug(query);
      this.model.findOne(query, function(err, result) {
        if (err) {
          debug(err);
          return done(err);
        }
        if (result && submission._id && (result._id.toString() === submission._id)) {
          return done();
        }
        if (result) {
          return done(new Error(component.label + ' must be unique.'));
        }

        done();
      });
    }.bind(this), function(err) {
      if (err) {
        return next(err.message);
      }

      joiValidate();
    });
  }
  else {
    joiValidate();
  }
};

module.exports = Validator;
<|endoftext|>


<commit_before>/*
 * responsive-carousel
 * https://github.com/filamentgroup/responsive-carousel
 *
 * Copyright (c) 2012 Filament Group, Inc.
 * Licensed under the MIT, GPL licenses.
 */

(function($) {

	var pluginName = "carousel",
		initSelector = "." + pluginName,
		transitionAttr = "data-transition",
		transitioningClass = pluginName + "-transitioning",
		itemClass = pluginName + "-item",
		activeClass = pluginName + "-active",
		prevClass = pluginName + "-item-prev",
		nextClass = pluginName + "-item-next",
		inClass = pluginName + "-in",
		outClass = pluginName + "-out",
		navClass =  pluginName + "-nav",
		prototype,
		cssTransitionsSupport = (function(){
			var prefixes = "webkit Moz O Ms".split( " " ),
				supported = false,
				property;

			while( prefixes.length ){
				property = prefixes.shift() + "Transition";

				if ( property in document.documentElement.style !== undefined && property in document.documentElement.style !== false ) {
					supported = true;
					break;
				}
			}
			return supported;
		}()),
		methods = {
			_create: function(){
				$( this )
					.trigger( "beforecreate." + pluginName )
					[ pluginName ]( "_init" )
					[ pluginName ]( "_addNextPrev" )
					.trigger( "create." + pluginName );
			},

			_init: function(){
				var trans = $( this ).attr( transitionAttr );

				if( !trans ){
					cssTransitionsSupport = false;
				}

				$( this )
					.addClass(
						pluginName +
						" " + ( trans ? pluginName + "-" + trans : "" ) + " "
					)
					.children()
					.addClass( itemClass )
					.first()
					.addClass( activeClass );

				$(this)[ pluginName ]( "_addNextPrevClasses" );
			},

			_addNextPrevClasses: function(){
				var $items = $( this ).find( "." + itemClass ),
					$active = $items.filter( "." + activeClass ),
					$next = $active.next( "." + itemClass ),
					$prev = $active.prev( "." + itemClass );

				if( !$next.length ){
					$next = $items.first().not( "." + activeClass );
				}
				if( !$prev.length ){
					$prev = $items.last().not( "." + activeClass );
				}

				$items.removeClass( prevClass + " " + nextClass );
				$prev.addClass( prevClass );
				$next.addClass( nextClass );

			},

			next: function(){
				$( this )[ pluginName ]( "goTo", "+1" );
			},

			prev: function(){
				$( this )[ pluginName ]( "goTo", "-1" );
			},

			goTo: function( num ){

				var $self = $(this),
					trans = $self.attr( transitionAttr ),
					reverseClass = " " + pluginName + "-" + trans + "-reverse";

				// clean up children
				$( this ).find( "." + itemClass ).removeClass( [ outClass, inClass, reverseClass ].join( " " ) );

				var $from = $( this ).find( "." + activeClass ),
					prevs = $from.index(),
					activeNum = ( prevs < 0 ? 0 : prevs ) + 1,
					nextNum = typeof( num ) === "number" ? num : activeNum + parseFloat(num),
					index = nextNum - 1,
					carouselItems = $( this ).find( ".carousel-item" ),
					$to = carouselItems.eq( index ),
					reverse = ( typeof( num ) === "string" && !(parseFloat(num)) ) || nextNum > activeNum ? "" : reverseClass;

				if( !prototype._isWrapped( this ) ) {
					// if the request index is larger than the set of carousel items or smaller than zero
					// and the carousel has been anotated correctly, prevent wrapping
					if( (index < 0 || index > (carouselItems.length - 1))) {
						return;
					}

					if( index === carouselItems.length - 1 ){
						$self.find( "a.next" ).addClass( "disabled" );
					}

					if( index === 0 ){
						$self.find( "a.prev" ).addClass( "disabled" );
					}

					if( index > 0 && index < carouselItems.length - 1 ) {
						$self.find( "a.next" ).removeClass( "disabled" );
						$self.find( "a.prev" ).removeClass( "disabled" );
					}
				}

				if( !$to.length ){
					$to = $( this ).find( "." + itemClass )[ reverse.length ? "last" : "first" ]();
				}

				if( cssTransitionsSupport ){
					$self[ pluginName ]( "_transitionStart", $from, $to, reverse );
				} else {
					$to.addClass( activeClass );
					$self[ pluginName ]( "_transitionEnd", $from, $to, reverse );
				}

				// added to allow pagination to track
				$self.trigger( "goto." + pluginName, $to );
			},

			update: function(){
				$(this).children().not( "." + navClass ).addClass( itemClass );

				return $(this).trigger( "update." + pluginName );
			},

			_transitionStart: function( $from, $to, reverseClass ){
				var $self = $(this);

				$to.one( navigator.userAgent.indexOf( "AppleWebKit" ) > -1 ? "webkitTransitionEnd" : "transitionend otransitionend", function(){
					$self[ pluginName ]( "_transitionEnd", $from, $to, reverseClass );
				});

				$(this).addClass( reverseClass );
				$from.addClass( outClass );
				$to.addClass( inClass );
			},

			_transitionEnd: function( $from, $to, reverseClass ){
				$( this ).removeClass( reverseClass );
				$from.removeClass( outClass + " " + activeClass );
				$to.removeClass( inClass ).addClass( activeClass );
				$( this )[ pluginName ]( "_addNextPrevClasses" );
			},

			_bindEventListeners: function(){
				var $elem = $( this )
					.bind( "click", function( e ){
						var targ = $( e.target ).closest( "a[href='#next'],a[href='#prev']" );
						if( targ.length ){
							$elem[ pluginName ]( targ.is( "[href='#next']" ) ? "next" : "prev" );
							e.preventDefault();
						}
					});

				return this;
			},

			_addNextPrev: function(){
				var $this = $( this ), nav;

				nav = "" +
					"Prev" +
					"Next" +
					"";

				// TODO this is silly
				if( !prototype._isWrapped( this ) ) {
					nav = nav.replace( /class='prev'/, "class='prev disabled'");
				}

				return $this.append( nav )[ pluginName ]( "_bindEventListeners" );
			},

			_isWrapped: function( element ) {
				return $( element ).attr( "data-loop" ) !== "false";
			},

			destroy: function(){
				// TODO
			}
		};

	// Collection method.
	$.fn[ pluginName ] = function( arrg, a, b, c ) {
		return this.each(function() {

			// if it's a method
			if( arrg && typeof( arrg ) === "string" ){
				return $.fn[ pluginName ].prototype[ arrg ].call( this, a, b, c );
			}

			// don't re-init
			if( $( this ).data( pluginName + "data" ) ){
				return $( this );
			}

			// otherwise, init
			$( this ).data( pluginName + "active", true );
			$.fn[ pluginName ].prototype._create.apply( this, arguments);
		});
	};

	// add methods
	prototype = $.extend( $.fn[ pluginName ].prototype, methods );
}(jQuery));
<commit_msg>switch method to loop noun<commit_after>/*
 * responsive-carousel
 * https://github.com/filamentgroup/responsive-carousel
 *
 * Copyright (c) 2012 Filament Group, Inc.
 * Licensed under the MIT, GPL licenses.
 */

(function($) {

	var pluginName = "carousel",
		initSelector = "." + pluginName,
		transitionAttr = "data-transition",
		transitioningClass = pluginName + "-transitioning",
		itemClass = pluginName + "-item",
		activeClass = pluginName + "-active",
		prevClass = pluginName + "-item-prev",
		nextClass = pluginName + "-item-next",
		inClass = pluginName + "-in",
		outClass = pluginName + "-out",
		navClass =  pluginName + "-nav",
		prototype,
		cssTransitionsSupport = (function(){
			var prefixes = "webkit Moz O Ms".split( " " ),
				supported = false,
				property;

			while( prefixes.length ){
				property = prefixes.shift() + "Transition";

				if ( property in document.documentElement.style !== undefined && property in document.documentElement.style !== false ) {
					supported = true;
					break;
				}
			}
			return supported;
		}()),
		methods = {
			_create: function(){
				$( this )
					.trigger( "beforecreate." + pluginName )
					[ pluginName ]( "_init" )
					[ pluginName ]( "_addNextPrev" )
					.trigger( "create." + pluginName );
			},

			_init: function(){
				var trans = $( this ).attr( transitionAttr );

				if( !trans ){
					cssTransitionsSupport = false;
				}

				$( this )
					.addClass(
						pluginName +
						" " + ( trans ? pluginName + "-" + trans : "" ) + " "
					)
					.children()
					.addClass( itemClass )
					.first()
					.addClass( activeClass );

				$(this)[ pluginName ]( "_addNextPrevClasses" );
			},

			_addNextPrevClasses: function(){
				var $items = $( this ).find( "." + itemClass ),
					$active = $items.filter( "." + activeClass ),
					$next = $active.next( "." + itemClass ),
					$prev = $active.prev( "." + itemClass );

				if( !$next.length ){
					$next = $items.first().not( "." + activeClass );
				}
				if( !$prev.length ){
					$prev = $items.last().not( "." + activeClass );
				}

				$items.removeClass( prevClass + " " + nextClass );
				$prev.addClass( prevClass );
				$next.addClass( nextClass );

			},

			next: function(){
				$( this )[ pluginName ]( "goTo", "+1" );
			},

			prev: function(){
				$( this )[ pluginName ]( "goTo", "-1" );
			},

			goTo: function( num ){

				var $self = $(this),
					trans = $self.attr( transitionAttr ),
					reverseClass = " " + pluginName + "-" + trans + "-reverse";

				// clean up children
				$( this ).find( "." + itemClass ).removeClass( [ outClass, inClass, reverseClass ].join( " " ) );

				var $from = $( this ).find( "." + activeClass ),
					prevs = $from.index(),
					activeNum = ( prevs < 0 ? 0 : prevs ) + 1,
					nextNum = typeof( num ) === "number" ? num : activeNum + parseFloat(num),
					index = nextNum - 1,
					carouselItems = $( this ).find( ".carousel-item" ),
					$to = carouselItems.eq( index ),
					reverse = ( typeof( num ) === "string" && !(parseFloat(num)) ) || nextNum > activeNum ? "" : reverseClass;

				if( !prototype._isLooped( this ) ) {
					// if the request index is larger than the set of carousel items or smaller than zero
					// and the carousel has been anotated correctly, prevent wrapping
					if( (index < 0 || index > (carouselItems.length - 1))) {
						return;
					}

					if( index === carouselItems.length - 1 ){
						$self.find( "a.next" ).addClass( "disabled" );
					}

					if( index === 0 ){
						$self.find( "a.prev" ).addClass( "disabled" );
					}

					if( index > 0 && index < carouselItems.length - 1 ) {
						$self.find( "a.next" ).removeClass( "disabled" );
						$self.find( "a.prev" ).removeClass( "disabled" );
					}
				}

				if( !$to.length ){
					$to = $( this ).find( "." + itemClass )[ reverse.length ? "last" : "first" ]();
				}

				if( cssTransitionsSupport ){
					$self[ pluginName ]( "_transitionStart", $from, $to, reverse );
				} else {
					$to.addClass( activeClass );
					$self[ pluginName ]( "_transitionEnd", $from, $to, reverse );
				}

				// added to allow pagination to track
				$self.trigger( "goto." + pluginName, $to );
			},

			update: function(){
				$(this).children().not( "." + navClass ).addClass( itemClass );

				return $(this).trigger( "update." + pluginName );
			},

			_transitionStart: function( $from, $to, reverseClass ){
				var $self = $(this);

				$to.one( navigator.userAgent.indexOf( "AppleWebKit" ) > -1 ? "webkitTransitionEnd" : "transitionend otransitionend", function(){
					$self[ pluginName ]( "_transitionEnd", $from, $to, reverseClass );
				});

				$(this).addClass( reverseClass );
				$from.addClass( outClass );
				$to.addClass( inClass );
			},

			_transitionEnd: function( $from, $to, reverseClass ){
				$( this ).removeClass( reverseClass );
				$from.removeClass( outClass + " " + activeClass );
				$to.removeClass( inClass ).addClass( activeClass );
				$( this )[ pluginName ]( "_addNextPrevClasses" );
			},

			_bindEventListeners: function(){
				var $elem = $( this )
					.bind( "click", function( e ){
						var targ = $( e.target ).closest( "a[href='#next'],a[href='#prev']" );
						if( targ.length ){
							$elem[ pluginName ]( targ.is( "[href='#next']" ) ? "next" : "prev" );
							e.preventDefault();
						}
					});

				return this;
			},

			_addNextPrev: function(){
				var $this = $( this ), nav;

				nav = "" +
					"Prev" +
					"Next" +
					"";

				if( !prototype._isLooped( this ) && $(this)) {
					// TODO this is silly
					nav = nav.replace( /class='prev'/, "class='prev disabled'");
				}

				return $this.append( nav )[ pluginName ]( "_bindEventListeners" );
			},

			_isLooped: function( element ) {
				return $( element ).attr( "data-loop" ) !== "false";
			},

			destroy: function(){
				// TODO
			}
		};

	// Collection method.
	$.fn[ pluginName ] = function( arrg, a, b, c ) {
		return this.each(function() {

			// if it's a method
			if( arrg && typeof( arrg ) === "string" ){
				return $.fn[ pluginName ].prototype[ arrg ].call( this, a, b, c );
			}

			// don't re-init
			if( $( this ).data( pluginName + "data" ) ){
				return $( this );
			}

			// otherwise, init
			$( this ).data( pluginName + "active", true );
			$.fn[ pluginName ].prototype._create.apply( this, arguments);
		});
	};

	// add methods
	prototype = $.extend( $.fn[ pluginName ].prototype, methods );
}(jQuery));
<|endoftext|>


<commit_before>(function (ng, app) {
    app.config(['$stateProvider', function ($stateProvider) {
        $stateProvider.state('it-system.usage.wishes', {
            url: '/wishes',
            templateUrl: 'partials/it-system/tab-wishes.html',
            controller: 'system.EditWishes',
            resolve: {
                user: ['userService', function (userService) {
                    return userService.getUser();
                }],
                wishes: ['$http', '$stateParams', 'userService', function ($http, $stateParams, userService) {
                    return userService.getUser().then(function (user) {
                        return $http.get('api/wish/?userId=' + user.id + '&usageId=' + $stateParams.id)
                            .then(function (result) {
                                return result.data.response;
                            });
                    });
                }]
            }
        });
    }]);

    app.controller('system.EditWishes', ['$rootScope', '$scope', '$http', '$state', '$stateParams', 'notify', 'wishes', 'user',
        function ($rootScope, $scope, $http, $state, $stateParams, notify, wishes, user) {
            $scope.user = user;
            $scope.wishes = wishes;

            function clear() {
                $scope.text = '';
                $scope.isPublic = false;
            }

            $scope.save = function () {
                var payload = {
                    text: $scope.text,
                    isPublic: $scope.isPublic,
                    userId: user.id,
                    itSystemUsageId: $stateParams.id
                };

                var msg = notify.addInfoMessage("Gemmer... ");
                $http.post('api/wish', payload).success(function () {
                    msg.toSuccessMessage("Ønsket er gemt!");
                    clear();
                    $state.transitionTo($state.current, $stateParams, {
                        reload: true,
                        inherit: false,
                        notify: true
                    });
                }).error(function () {
                    msg.toErrorMessage("Fejl! Ønsket kunne ikke gemmes!");
                });
            };

            $scope.delete = function(id) {
                var msg = notify.addInfoMessage("Gemmer... ");
                $http.delete('api/wish/' + id).success(function() {
                    msg.toSuccessMessage("Ønsket er slettet!");
                    $state.go('.', null, { reload: true });
                }).error(function() {
                    msg.toErrorMessage("Fejl! Ønsket kunne ikke slettes!");
                });
            }
        }]);
})(angular, app);<commit_msg>Added comments and clean up<commit_after>(function (ng, app) {
    app.config(['$stateProvider', function ($stateProvider) {
        $stateProvider.state('it-system.usage.wishes', {
            url: '/wishes',
            templateUrl: 'partials/it-system/tab-wishes.html',
            controller: 'system.EditWishes',
            resolve: {
                user: ['userService', function (userService) {
                    return userService.getUser();
                }],
                wishes: ['$http', '$stateParams', 'userService', function ($http, $stateParams, userService) {
                    return userService.getUser().then(function (user) {
                        return $http.get('api/wish/?userId=' + user.id + '&usageId=' + $stateParams.id)
                            .then(function (result) {
                                return result.data.response;
                            });
                    });
                }]
            }
        });
    }]);

    app.controller('system.EditWishes', ['$rootScope', '$scope', '$http', '$state', '$stateParams', 'notify', 'wishes', 'user',
        function ($rootScope, $scope, $http, $state, $stateParams, notify, wishes, user) {
            $scope.user = user;
            $scope.wishes = wishes;

            $scope.save = function () {
                var payload = {
                    text: $scope.text,
                    isPublic: $scope.isPublic,
                    userId: user.id,
                    itSystemUsageId: $stateParams.id
                };

                var msg = notify.addInfoMessage("Gemmer... ");
                $http.post('api/wish', payload).success(function () {
                    msg.toSuccessMessage("Ønsket er gemt!");
                    // reload current state to reflect changes
                    $state.go('.', null, { reload: true });
                }).error(function () {
                    msg.toErrorMessage("Fejl! Ønsket kunne ikke gemmes!");
                });
            };

            $scope.delete = function(id) {
                var msg = notify.addInfoMessage("Gemmer... ");
                $http.delete('api/wish/' + id).success(function() {
                    msg.toSuccessMessage("Ønsket er slettet!");
                    // reload current state to reflect changes
                    $state.go('.', null, { reload: true });
                }).error(function() {
                    msg.toErrorMessage("Fejl! Ønsket kunne ikke slettes!");
                });
            }
        }]);
})(angular, app);
<|endoftext|>


<commit_before>var Readable     = require('stream').Readable
var iherits      = require("util").inherits
var fs           = require("fs")
var ReadStream   = fs.ReadStream
    function  Bundle (name,options) {
        if (!name) throw new Error("Name Bundle Required")
        Readable.call(this,options)
        this.name           = name
        this.options        = options
        this._sources       = []
        this._index         = 0
    };

    iherits(Bundle,Readable)
    module.exports = Bundle
    Bundle.prototype.addFiles           = function (array) {
        var notArray  = !(array instanceof Array)
        if (notArray) throw new Error ("Required an array containing paths")
        this._sources = array
    }

    Bundle.prototype._isLastSource      = function (            ) { return this._index === this._sources.length;}
    Bundle.prototype._read              = function () {
        if (this._isLastSource()){ this.push(null); return }
        var path            = this._sources[this._index]
        this.currentStream  = this.currentStream || new ReadStream(path,this.options)
        this.currentStream.once("data", (function (chunk) {
                if (typeof chunk === "string") chunk+=("\r\n")
                //this.resume()
                this.push(new Buffer(chunk,this.options))
                this._index++
                this.currentStream = false
            }).bind(this))

    }<commit_msg>Added support to a chunked stream.<commit_after>var Readable     = require('stream').Readable
var iherits      = require("util").inherits
var fs           = require("fs")
var ReadStream   = fs.FileReadStream
    function  Bundle (name,options) {
        if (!name) throw new Error("Name Bundle Required")
        Readable.call(this,options)
        this.name           = name
        this.options        = options
        this._sources       = []
        this._index         = 0
        this.__fRead        = 0
        this._dbg           = 0
    };

    iherits(Bundle,Readable)
    module.exports = Bundle
    Bundle.prototype.addName            = function (name) {
        require("path").parse(name).extname();

    }
    Bundle.prototype.addFiles           = function (array) {
        var notArray  = !(array instanceof Array)
        if (notArray) throw new Error ("Required an array containing paths")
        this._sources = array
    }

    Bundle.prototype._isLastSource      = function (            ) { return this._index === this._sources.length;}
    Bundle.prototype._read              = function (chunkSize) {
        if (this._isLastSource()){ this.push(null); return }
        var filePath            = this._sources[this._index]
        if (chunkSize) {
            var open    = fs.open
            var stat    = fs.stat
            var read    = fs.read
            var close   = fs.close

                open(filePath,"r", (function (err, fd) {
                    if (err) throw err
                    stat(filePath, (function (err, stats) {
                        if (err) throw err
                    var sizeofFile  = stats.size
                    var read        = this.__fRead
                        if (read !== sizeofFile){
                            if ((read+chunkSize)>sizeofFile) chunkSize = sizeofFile-read
                            var buffer = new Buffer(chunkSize,this.options.encoding)
                                fs.read(fd,buffer,0,chunkSize,read, (function (err,l,buff) {
                                    if(err) throw err
                                    this._dbg++
                                    this.push(buff)
                                    this.__fRead+=chunkSize
                                }).bind(this))
                        } else {
                            this.__fRead = 0
                            this._index++
                            this.push(new Buffer("\r\n",this.options.encoding))
                            close(fd)
                            console.log(" %s | %s | %s: %s | Chunk: %s | Read: %s:%s |   %s:%s ",this._index,this._dbg,filePath,sizeofFile,chunkSize,read,typeof read,sizeofFile, typeof sizeofFile)

                        }

                    }).bind(this))
                }).bind(this))

        } else {
            this.currentStream  = this.currentStream || new ReadStream(filePath,this.options)
            this.currentStream.once("data", (function (chunk) {
                if (typeof chunk === "string") chunk+=("\r\n")
                this.push(new Buffer(chunk,this.options.encoding))
                this._index++
                this.currentStream = false
            }).bind(this))
        }
    }<|endoftext|>


<commit_before>/** @param {Creep} creep **/
var roleUpgrader = {

    run: function(creep) {

        var start_state = creep.memory.state;
        var new_state=0;

        switch (start_state) {
        case 0: // Gathering.  When we're at capacity, start moving.
            if(creep.carry.energy == creep.carryCapacity) {
            var new_state = 1;
                }
            break;
        case 1: // Distributing energy.  When we hit 0, go back to gathering.
            if (creep.carry.energy == 0) {
            var new_state = 0;}
            else { new_state = 1;
                 }
            break;
        };

        creep.memory.state = new_state;

         console.log('new state ' + creep.memory.state);
	    switch (new_state) {
        case 0: // Take resources, either from a container or node
            var sources = creep.room.find(FIND_SOURCES);
            console.log('find sources ' );
            if(creep.harvest(sources[1]) == ERR_NOT_IN_RANGE) {
                creep.moveTo(sources[1]);
            }
	    break;
	    case 1: // Upgrade the node controller
	           console.log('upgrade Controller ' );
	           if(creep.upgradeController(creep.room.controller) == ERR_NOT_IN_RANGE) {
                creep.moveTo(creep.room.controller);
	           }
	    break;
        }

	}

};
module.exports = roleUpgrader;<commit_msg>Added Movement optimisation<commit_after>/** @param {Creep} creep **/
var roleUpgrader = {

    run: function(creep) {

        var start_state = creep.memory.state;
        var new_state=0;

        switch (start_state) {
        case 0: // Gathering.  When we're at capacity, start moving.
            if(creep.carry.energy == creep.carryCapacity) {
            var new_state = 1;
                }
            break;
        case 1: // Distributing energy.  When we hit 0, go back to gathering.
            if (creep.carry.energy == 0) {
            var new_state = 0;}
            else { new_state = 1;
                 }
            break;
        };

        creep.memory.state = new_state;

         console.log('new state ' + creep.memory.state);
	    switch (new_state) {
        case 0: // Take resources, either from a container or node
            var sources = creep.room.find(FIND_SOURCES);
            console.log('find sources ' );
            if(creep.harvest(sources[1]) == ERR_NOT_IN_RANGE) {
				if(Game.cpu.tickLimit - Game.cpu.getUsed() > 20) {
					creep.moveTo(sources[1]);
					}
				else {
                    creep.moveTo(sources[1],{reusePath: 10});
			     }
			}
	    break;
	    case 1: // Upgrade the node controller
	           console.log('upgrade Controller ' );

	        if(creep.upgradeController(creep.room.controller) == ERR_NOT_IN_RANGE) {
				if(Game.cpu.tickLimit - Game.cpu.getUsed() > 20) {
					creep.moveTo(creep.room.controller);
					}
				else {
                    creep.moveTo(creep.room.controller,{reusePath: 10});
			     }
	           }
	    break;
        }

	}

};
module.exports = roleUpgrader;<|endoftext|>


<commit_before>/**
 * @package admiral
 * @author Andrew Munsell <andrew@wizardapps.net>
 * @copyright 2015 WizardApps
 */

var moment = require('moment'),
    uuid = require('node-uuid'),
    validate = require('validate.js');

exports = module.exports = function(app, config) {
    /**
     * Get the applications
     */
    app.get('/v1/applications', function(req, res) {
        config.getAsync('/applications', { recursive: true })
            .spread(function(result) {
                res
                    .header('Cache-Control', 'private, max-age=60')
                    .json((result.node.nodes || [] ).map(function(node) {
                        return JSON.parse(node.value);
                    }));
            })
            .catch(function(err) {
                if(err.errorCode == 100) {
                    res
                        .json([]);
                } else {
                    res
                        .status(500)
                        .json({
                            error: 500,
                            message: 'There was a problem retrieving the list of applications.'
                        });
                }
            })
            .finally(function() {
                res.end();
            });
    });

    /**
     * Create a new application
     */
    app.post('/v1/applications', function(req, res) {
        var constraints = {
            name: {
                presence: true,
                length: {
                    maximum: 36
                }
            }
        };

        var errors = validate(req.body, constraints);
        if(errors) {
            return res
                .status(400)
                .json(errors)
                .end();
        }

        var id = uuid.v4();

        var app = {
            id: id,
            name: req.body.name,
            createdAt: moment().toISOString()
        };

        config.setAsync('/applications/' + id, JSON.stringify(app))
            .spread(function(result) {
                res
                    .json(JSON.parse(result.node.value));
            })
            .catch(function(err) {
                res
                    .status(500)
                    .json({
                        error: 500,
                        message: 'There was a problem creating the application.'
                    });
            })
            .finally(function() {
                res.end();
            });
    });

    /**
     * Get information for the specified application
     */
    app.get('/v1/applications/:id', function(req, res) {
        config.getAsync('/applications/' + req.params.id)
            .spread(function(result) {
                res
                    .header('Cache-Control', 'private, max-age=60')
                    .json(JSON.parse(result.node.value));
            })
            .catch(function(err) {
                if(err.errorCode == 100) {
                    res
                        .status(404)
                        .json({
                            error: 404,
                            message: 'The specified application doesn\'t exist.'
                        })
                } else {
                    res
                        .status(500)
                        .json({
                            error: 500,
                            message: 'There was a problem retrieving the applications.'
                        });
                }
            })
            .finally(function() {
                res.end();
            });
    });

    /**
     * Destroy the specified application
     */
    app.delete('/v1/applications/:id', function(req, res) {
        config.getAsync('/applications/' + req.params.id)
            .spread(function(result) {
                return config.delAsync('/applications/' + req.params.id);
            })
            .spread(function(result) {
                res.json({
                    ok: true
                });
            })
            .catch(function(err) {
                if(err.errorCode == 100) {
                    res
                        .status(404)
                        .json({
                            error: 404,
                            message: 'The specified application could not be found.'
                        });
                } else {
                    res
                        .status(500)
                        .json({
                            error: 500,
                            message: 'There was a problem deleting the specified application.'
                        });
                }
            })
            .finally(function() {
                res.end();
            });
    });
};

exports['@singleton'] = true;
exports['@require'] = ['app', 'config'];<commit_msg>Add endpoint to get the services of an application<commit_after>/**
 * @package admiral
 * @author Andrew Munsell <andrew@wizardapps.net>
 * @copyright 2015 WizardApps
 */

var moment = require('moment'),
    uuid = require('node-uuid'),
    validate = require('validate.js');

exports = module.exports = function(app, config) {
    /**
     * Get the applications
     */
    app.get('/v1/applications', function(req, res) {
        config.getAsync('/applications', { recursive: true })
            .spread(function(result) {
                res
                    .header('Cache-Control', 'private, max-age=60')
                    .json((result.node.nodes || [] ).map(function(node) {
                        return JSON.parse(node.value);
                    }));
            })
            .catch(function(err) {
                if(err.errorCode == 100) {
                    res
                        .json([]);
                } else {
                    res
                        .status(500)
                        .json({
                            error: 500,
                            message: 'There was a problem retrieving the list of applications.'
                        });
                }
            })
            .finally(function() {
                res.end();
            });
    });

    /**
     * Create a new application
     */
    app.post('/v1/applications', function(req, res) {
        var constraints = {
            name: {
                presence: true,
                length: {
                    maximum: 36
                }
            }
        };

        var errors = validate(req.body, constraints);
        if(errors) {
            return res
                .status(400)
                .json(errors)
                .end();
        }

        var id = uuid.v4();

        var app = {
            id: id,
            name: req.body.name,
            createdAt: moment().toISOString()
        };

        config.setAsync('/applications/' + id, JSON.stringify(app))
            .spread(function(result) {
                res
                    .json(JSON.parse(result.node.value));
            })
            .catch(function(err) {
                res
                    .status(500)
                    .json({
                        error: 500,
                        message: 'There was a problem creating the application.'
                    });
            })
            .finally(function() {
                res.end();
            });
    });

    /**
     * Get information for the specified application
     */
    app.get('/v1/applications/:id', function(req, res) {
        config.getAsync('/applications/' + req.params.id)
            .spread(function(result) {
                res
                    .header('Cache-Control', 'private, max-age=60')
                    .json(JSON.parse(result.node.value));
            })
            .catch(function(err) {
                if(err.errorCode == 100) {
                    res
                        .status(404)
                        .json({
                            error: 404,
                            message: 'The specified application doesn\'t exist.'
                        })
                } else {
                    res
                        .status(500)
                        .json({
                            error: 500,
                            message: 'There was a problem retrieving the applications.'
                        });
                }
            })
            .finally(function() {
                res.end();
            });
    });

    /**
     * Get the services for the specified application
     */
    app.get('/v1/applications/:id/services', function(req, res) {
        config.getAsync('/services', { recursive: true })
            .spread(function(result) {
                return (result.node.nodes || [] ).map(function(node) {
                    return JSON.parse(node.value);
                });
            })
            .then(function(services) {
                return services.filter(function(service) {
                    return service.application == req.params.id;
                });
            })
            .then(function(services) {
                res
                    .header('Cache-Control', 'private, max-age=60')
                    .json(services);
            })
            .catch(function(err) {
                if(err.errorCode == 100 && err.error.cause.indexOf('/services') < 0) {
                    res
                        .status(404)
                        .json({
                            error: 404,
                            message: 'The specified application doesn\'t exist.'
                        });
                } else {
                    res
                        .status(500)
                        .json({
                            error: 500,
                            message: 'There was a problem retrieving the list of services.'
                        });
                }
            })
            .finally(function() {
                res.end();
            });
    });

    /**
     * Destroy the specified application
     */
    app.delete('/v1/applications/:id', function(req, res) {
        config.getAsync('/applications/' + req.params.id)
            .spread(function(result) {
                return config.delAsync('/applications/' + req.params.id);
            })
            .spread(function(result) {
                res.json({
                    ok: true
                });
            })
            .catch(function(err) {
                if(err.errorCode == 100) {
                    res
                        .status(404)
                        .json({
                            error: 404,
                            message: 'The specified application could not be found.'
                        });
                } else {
                    res
                        .status(500)
                        .json({
                            error: 500,
                            message: 'There was a problem deleting the specified application.'
                        });
                }
            })
            .finally(function() {
                res.end();
            });
    });
};

exports['@singleton'] = true;
exports['@require'] = ['app', 'config'];<|endoftext|>


<commit_before>define(['../utils'], function (utils) {
    function GroupingDataSource(delegate) {
        var self = this;
        this.delegate = delegate;
        for (var x in this.delegate) {
            if (!this[x] && (typeof this.delegate[x] === "function")) {
                this[x] = this.delegate[x].bind(this.delegate);
            }
        }
        if (delegate.isReady()) {
            this.load();
        }

        $(delegate).on("datachanged", function (event, data) {
            self.load();
            $(self).trigger(event.type, [data]);
        });

        $(delegate).on("dataloaded", this.load.bind(this));
        this.groups = [];
    }

    GroupingDataSource.prototype = {
        load: function () {
            this.parentByIdMap = {};
            this.updateView();
        },

        updateView: function () {
            var ds = this;
            var groupRows = this.groupRows = {};
            var rowToGroupMap = {};

            function group(nodes, groupings, parentGroupId, level) {
                if (groupings && groupings.length) {
                    var groupMap = {},
                        groups = [],
                        col = groupings[0],
                        f = col.groupProjection && col.groupProjection(nodes) || function (v) {
                                return v;
                            },
                        nextGroupings = groupings.slice(1);
                    for (var x = 0, l = nodes.length; x < l; x++) {
                        var g = f(utils.getValue(nodes[x], col.key));
                        var r = groupMap[g];
                        if (!r) {
                            groups.push(groupMap[g] = r = {
                                groupRow: true,
                                id: parentGroupId + g + ":",
                                description: g,
                                children: [],
                                _groupColumn: col,
                                _groupLevel: level,
                                parent: parentGroupId
                            });

                            r[col.key] = g;
                        }
                        r.children.push(nodes[x]);
                        ds.parentByIdMap[nodes[x].id] = r;
                        groupRows[r.id] = r;
                    }

                    for (var x = 0, l = groups.length; x < l; x++) {
                        groups[x].recordCount = groups[x].children.length;
                        groups[x].children = group(groups[x].children, nextGroupings, groups[x].id, level + 1);
                        ds.processGroup(groups[x]);
                    }

                    return groups;
                } else {
                    for (var x = 0, l = nodes.length; x < l; x++) {
                        rowToGroupMap[nodes[x].id] = parentGroupId;
                    }
                    return nodes;
                }
            }

            this.view = group(this.delegate.getData(), this.groups, "group:", 0);
            $(this).trigger("dataloaded");
        },

        group: function (groupings) {
            this.groups = groupings;
            if (this.isReady()) {
                this.updateView();
            }
        },

        getRecordById: function (id) {
            return this.groupRows[id] || this.delegate.getRecordById(id);
        },

        getData: function (start, end) {
            this.assertReady();
            if (start !== undefined || end !== undefined) {
                return this.view.slice(start, end);
            } else {
                return this.view;
            }
        },

        recordCount: function () {
            this.assertReady();
            return this.view.length;
        },

        isReady: function () {
            return this.view !== undefined;
        },

        assertReady: function () {
            if (!this.isReady()) {
                throw "Datasource not ready yet";
            }
        },

        parent: function (row) {
            return row.parent || this.parentByIdMap[row.id].id;
        },

        hasChildren: function (row) {
            var groupRow = this.groupRows[row.id];
            if (groupRow) {
                return groupRow.children && groupRow.children.length > 0;
            } else {
                return false;
            }
        },

        buildTree: function () {
            return this.getData();
        },

        processGroup: function(group) {

        }
    };

    return GroupingDataSource;

});
<commit_msg>AIR-401<commit_after>define(['../utils'], function (utils) {
    function GroupingDataSource(delegate) {
        var self = this;
        this.delegate = delegate;
        for (var x in this.delegate) {
            if (!this[x] && (typeof this.delegate[x] === "function")) {
                this[x] = this.delegate[x].bind(this.delegate);
            }
        }
        if (delegate.isReady()) {
            this.load();
        }

        $(delegate).on("datachanged", function (event, data) {
            self.load();
            $(self).trigger(event.type, [data]);
        });

        $(delegate).on("dataloaded", this.load.bind(this));
        this.groups = [];
    }

    GroupingDataSource.prototype = {
        load: function () {
            this.parentByIdMap = {};
            this.updateView();
        },

        updateView: function () {
            var ds = this;
            var groupRows = this.groupRows = {};
            var rowToGroupMap = {};

            function group(nodes, groupings, parentGroupId, level) {
                if (groupings && groupings.length) {
                    var groupMap = {},
                        groups = [],
                        col = groupings[0],
                        f = col.groupProjection && col.groupProjection(nodes) || function (v) {
                                return v;
                            },
                        nextGroupings = groupings.slice(1);
                    for (var x = 0, l = nodes.length; x < l; x++) {
                        var g = f(utils.getValue(nodes[x], col.key));
                        var r = groupMap[g];
                        if (!r) {
                            groups.push(groupMap[g] = r = {
                                groupRow: true,
                                id: parentGroupId + g + ":",
                                description: g,
                                children: [],
                                _groupColumn: col,
                                _groupLevel: level,
                                parent: parentGroupId
                            });

                            r[col.key] = g;
                        }
                        r.children.push(nodes[x]);
                        ds.parentByIdMap[nodes[x].id] = r;
                        groupRows[r.id] = r;
                    }

                    for (var x = 0, l = groups.length; x < l; x++) {
                        groups[x].recordCount = groups[x].children.length;
                        groups[x].children = group(groups[x].children, nextGroupings, groups[x].id, level + 1);
                        ds.processGroup(groups[x]);
                    }

                    return groups;
                } else {
                    for (var x = 0, l = nodes.length; x < l; x++) {
                        rowToGroupMap[nodes[x].id] = parentGroupId;
                    }
                    return nodes;
                }
            }

            if(this.groups && this.groups.length) {
                this.view = group(this.delegate.getData(), this.groups, "group:", 0);
            } else {
                // make sure view is a copy of the original data, and not original array itself, so when we sort stuff
                // we don't affect the original datasource
                this.view = this.delegate.getData().concat([]);
            }
            $(this).trigger("dataloaded");
        },

        group: function (groupings) {
            this.groups = groupings;
            if (this.isReady()) {
                this.updateView();
            }
        },

        getRecordById: function (id) {
            return this.groupRows[id] || this.delegate.getRecordById(id);
        },

        getData: function (start, end) {
            this.assertReady();
            if (start !== undefined || end !== undefined) {
                return this.view.slice(start, end);
            } else {
                return this.view;
            }
        },

        recordCount: function () {
            this.assertReady();
            return this.view.length;
        },

        isReady: function () {
            return this.view !== undefined;
        },

        assertReady: function () {
            if (!this.isReady()) {
                throw "Datasource not ready yet";
            }
        },

        parent: function (row) {
            return row.parent || this.parentByIdMap[row.id].id;
        },

        hasChildren: function (row) {
            var groupRow = this.groupRows[row.id];
            if (groupRow) {
                return groupRow.children && groupRow.children.length > 0;
            } else {
                return false;
            }
        },

        buildTree: function () {
            return this.getData();
        },

        processGroup: function(group) {

        }
    };

    return GroupingDataSource;

});
<|endoftext|>