<commit_before>(function () {
    var app = angular.module('mainApp', ['ngRoute', 'config', 'ngScrollbars', 'services', 'directives', 'chart.js', 'angularFileUpload']);
    app.controller('mainCtrl', ['$scope', 'restful', '$location', '$route', '$routeParams', '$interval', 'FileUploader', '$http', '$document', '$filter', function ($scope, restful, $location, $route, $routeParams, $interval, FileUploader, $http, $document, $filter) {

        $scope.todayDateTime = new Date;

        /*Append loading page druing data fetching*/
        var loadingPage = {
            ready: function () {
                angular.element('.loading .text').innerHTML = 'Fetching data...';
                var interval = $interval(function () {
                    if ($http.pendingRequests < 1) {
                        angular.element('.loading').addClass('hidden');
                        $document.find('body').css('overflow', 'auto');
                        $interval.cancel(interval);
                    }
                }, 1000);
            }
        };
        /*Hide it when content is loaded*/
        $scope.$on('$viewContentLoaded', function () {
            loadingPage.ready();
        });

        /*Change route to dashboard*/
        $scope.redirectToDash = function () {
            $location.path('/dashboard');
        };

        /*Limits for lists*/
        $scope.limit = {
            users: 5,
            group: 3,
            log: 5,
            components: 5
        };

        /*Menu elements*/
        $scope.menu = {
            status: true,
            hide: function () {
                this.status = !this.status;
            },
            active: function (url) {
                if (url.indexOf(':siteID') === -1) {
                    var arr = angular.element('#navigation a').removeClass('active-li');
                    for (var i = 0; i < arr.length; i++) {
                        if (arr[i].href.indexOf(url) !== -1)
                            var active = arr[i];
                    }
                    active.className += ' active-li';
                }
            }
        };
        /*Init stats objects*/
        $scope.stats = {
            apps: {},
            users: {},
            components: {},
            logs: {}
        }

        /*Filter apps by params in a route*/
        $scope.filterParams = {
            manage: function () {
                if ($routeParams.siteID)
                    return parseInt($routeParams.siteID);
                else return '';
            }
        };

        var generateWeekStats = function (array) {
            var today = new Date;
            var week = [];
            week.push($filter('date')(today, 'MM/dd/yyyy'));
            var count = [0, 0, 0, 0, 0];
            for (var i = 0; i < 4; i++) {
                today.setDate(today.getDate() - 1);
                week.push($filter('date')(today, 'MM/dd/yyyy'));
            }
            week.reverse();
            for (var i = 0; i < array.length; i++) {
                var day = $filter('date')(new Date(array[i].date.replace(' ', 'T')), 'MM/dd/yyyy');
                var foundAt = week.indexOf(day);
                if (foundAt !== -1)
                    count[foundAt] += 1;
            }
            return {
                week: week,
                count: count
            }
        }

        var generateTwoDaysStats = function (array) {
            var date = new Date;
            var today = $filter('date')(date, 'MM/dd/yyyy')
            var yesterday = $filter('date')(date.setDate(date.getDate() - 1), 'MM/dd/yyyy')
            var days = [today, yesterday];
            var count = [0, 0];
            for (var i = 0; i < array.length; i++) {
                var day = $filter('date')(new Date(array[i].date.replace(' ', 'T')), 'MM/dd/yyyy');
                var foundAt = days.indexOf(day);
                if (foundAt !== -1)
                    count[foundAt] += 1;
            }
            return {
                count: count.reverse()
            }
        };

        var generateTripleDataStats = function (array) {
            var date = new Date;
            var today = $filter('date')(date, 'MM/dd/yyyy');
            var count = {
                'drop': 0,
                'edit': 0
            };
            for (var i = 0; i < array.length; i++) {
                var day = $filter('date')(new Date(array[i].date.replace(' ', 'T')), 'MM/dd/yyyy');
                if (today == day)
                    if (array[i].content.indexOf('updated') !== -1)
                        count.edit += 1;
                    else
                        count.drop += 1;
            }
            return count;
        }

        /*Get data model*/
        var update = {
            me: function () {
                restful.get('me/user').then(function (response) {
                    $scope.current_user = response['objects'][0];
                });
            },
            apps: function () {
                restful.get('app').then(function (response) {
                    var apps = $scope.apps = response['objects'];
                    $scope.stats.apps = {
                        labels: generateWeekStats(apps).week,
                        series: ['Applications created'],
                        data: [generateWeekStats(apps).count]
                    }
                });
            },
            users: function () {
                restful.get('user').then(function (response) {
                    var users = $scope.users = response['objects'];
                    $scope.stats.users = {
                        labels: ['Yesterday', 'Today'],
                        series: ['Registered'],
                        data: [generateTwoDaysStats(users).count]
                    }
                });
            },
            groups: function () {
                restful.get('group').then(function (response) {
                    $scope.groups = response['objects'];
                });
            },
            logs: function () {
                restful.get('log').then(function (response) {
                    var logs = $scope.logs = response['objects'];
                    $scope.logs.todaysAct = generateTripleDataStats(logs).edit + generateTripleDataStats(logs).drop;
                    $scope.stats.logs = {
                        labels: ['Activity'],
                        series: ['Updates', 'Deletions'],
                        data: [
                    [generateTripleDataStats(logs).edit],
                    [generateTripleDataStats(logs).drop]
                  ]
                    }
                });
            },
            notes: function () {
                restful.get('note').then(function (response) {
                    $scope.notes = response['objects'];
                });
            },
            components: function () {
                restful.get('component').then(function (response) {
                    $scope.components = response['objects'];
                });
            },
            all: function () {
                this.me();
                this.apps();
                this.users();
                this.groups();
                this.logs();
                this.notes();
                this.components();
            }
        };
        update.all();
        /*Dashboard clock*/
        $scope.clockDate = {
            setup: new Date(),
            time: function () {
                if (this.setup.getMinutes() < 10)
                    return this.setup.getHours() > 9 ? this.setup.getHours() + ':0' + this.setup.getMinutes() : '0' + this.setup.getHours() + ':0' + this.setup.getMinutes();
                else
                    return this.setup.getHours() > 9 ? this.setup.getHours() + ':' + this.setup.getMinutes() : '0' + this.setup.getHours() + ':' + this.setup.getMinutes();
            },
            date: function () {
                return this.setup.getTime();
            },
            update: function () {
                this.setup = new Date();
            }
        };

        /*Refresh clock every 30s*/
        $interval(function () {
            $scope.clockDate.update()
        }, 30000)

        /*Applications methods*/
        $scope.newlink = {
            uploader: new FileUploader({
                url: 'api/upload/img',
                formData: [],
                removeAfterUpload: true,
                withCredentials: true,
                queueLimit: 1
            }),
            name: '',
            address: '',
            desc: '',
            img_link: '',
            order_id: '',
            beta: null,
            manageFill: function (name, link, desc, img_link, order_id, beta) {
                this.name = name;
                this.address = link;
                this.desc = desc;
                this.img_link = img_link;
                this.order_id = order_id;
                this.beta = beta;
            },
            add: function () {
                var img_link = $scope.clockDate.date();
                this.uploader.onBeforeUploadItem = function (item) {
                    item.formData.push({
                        filename: img_link
                    });
                }
                this.uploader.uploadItem(0);
                this.uploader.onSuccessItem = (item, response, status, headers) => {
                    var post_object = {
                        name: this.name,
                        link: this.address,
                        desc: this.desc,
                        creator_id: $scope.current_user.id,
                        img_link: img_link,
                    }
                    restful.post('app', post_object).then(function () {
                        update.apps();
                    });
                    this.clear();
                    this.status = true;
                    this.uploader.clearQueue();
                };
                this.uploader.onErrorItem = function (item, response, status, headers) {
                    console.log('Uplaoder: Error callback');
                    console.log(item);
                    console.log(response);
                    console.log(headers);
                };

                this.uploader.onCancelItem = function (item, response, status, headers) {
                    console.log('Uploader: Cancel callback');
                    console.log(item);
                    console.log(response);
                    console.log(headers);
                };

            },
            update: function (app_id) {
                if (this.uploader.queue.length > 0) {
                    this.img_link = new Date();
                    this.img_link = this.img_link.getTime();
                    this.uploader.onBeforeUploadItem = (item) => {
                        item.formData.push({
                            filename: this.img_link
                        });
                    }
                    this.uploader.uploadAll();
                    this.uploader.onSuccessItem = (item, response, status, headers) => {
                        console.log('Uploader: Success callback');
                        this.uploader.clearQueue();
                    }
                    this.uploader.onErrorItem = (item, response, status, headers) => {
                        //Add information for img uplaod error in DOM (html)
                        console.log('Uplaoder: Error callback');
                        console.log(item);
                        console.log(response);
                        console.log(headers);
                    };
                    this.uploader.onCancelItem = (item, response, status, headers) => {
                        //Add information for img uplaod error in DOM (html)
                        console.log('Uploader: Cancel callback');
                        console.log(item);
                        console.log(response);
                        console.log(headers);
                    };
                }
                var post_object = {
                    name: this.name,
                    link: this.address,
                    desc: this.desc,
                    img_link: this.img_link,
                    order_id: this.order_id,
                    beta: this.beta,
                }
                restful.update('app', app_id, post_object).then(function () {
                    var log_object = {
                        content: 'A link #' + app_id + ' was updated',
                        author_id: $scope.current_user.id
                    }
                    update.apps();
                    restful.post('log', log_object).then(function () {
                        update.logs();
                    });
                });
                this.clear();
                this.status = true;
                $location.path('/links').replace();
            },
            hide: function (app_id, app_status) {
                var confirmResult = confirm("Do you want to change visibility of this app?");
                if (confirmResult) {
                    var hide = {
                        status: !app_status
                    }
                    restful.update('app', app_id, hide).then(function () {
                        var log_object = {
                            content: 'A link #' + app_id + ' was updated',
                            author_id: $scope.current_user.id
                        }
                        update.apps();
                        restful.post('log', log_object).then(function () {
                            update.logs();
                        });
                    });
                }
            },
            delete: function (app_id) {
                var confirmResult = confirm("Do you want to remove this app?");
                if (confirmResult) {
                    restful.delete("app", app_id).then(function () {
                        var log_object = {
                            content: 'A link #' + app_id + ' was removed',
                            author_id: $scope.current_user.id
                        }
                        update.apps();
                        restful.post('log', log_object).then(function () {
                            update.logs();
                        });
                    });
                }
            },
            clear: function () {
                this.name = '';
                this.address = '';
                this.desc = '';
            },
            status: false
        };

        $scope.user_inf = {
            uploader: new FileUploader({
                url: 'api/upload/avatar',
                formData: []
            }),
            manageFill(username, email) {
                this.username = username;
                this.email = email;
            },
            img_update: function () {
                if (this.uploader.queue.length > 0) {
                    this.uploader.uploadAll();
                    restful.update('user', $scope.current_user.id, {
                        avatar_url: 'avatar_' + $scope.current_user.id
                    }).then(function (response) {
                        this.uploader.clearQueue();
                        //window.location.reload();
                    });
                }
            },
            user_update: function () {
                if (this.password == null || this.password == this.pass_verify)
                    restful.post('auth/checkpass', {
                        pass: this.current_pass
                    }).then((response) => {
                        if (response == 'True') {
                            var user_info = {
                                username: this.username,
                                email: this.email,
                            }
                            restful.update('user', $scope.current_user.id, user_info).then((response) => {
                                update.me();
                                $scope.userFormSucces = true;
                                this.clear();
                            });
                            if (this.pass_verify != null && this.password != null && this.password == this.pass_verify)
                                restful.post('auth/changepass', {
                                    newpass: this.password
                                }).then(() => {
                                    window.location.reload();
                                })
                        } else
                            $scope.userFormWrongPass = true;
                    });
            },
            clear: function () {
                this.password = '';
                this.current_pass = '';
                this.pass_verify = '';
            }

        };

        $scope.note = {
            content: '',
            tag: '',
            add: function () {
                var post_note = {
                    content: this.content,
                    tag: this.tag,
                    owner_id: $scope.current_user.id,
                }
                restful.post('note', post_note).then(function () {
                    update.notes();
                });
                this.clear();
            },
            clear: function () {
                this.content = '';
                this.tag = '';
            }
        };

        $scope.orderArray = [1, 2, 3, 4, 5, 6, 7, 8, 9];
        /*
        If you use some variables in a couple places you probably
        want to reset them when you leave a page. That's why you should put
        those variables/functions below.
        */
        $scope.$on('$routeChangeStart', function (current, next) {
            $scope.newlink.clear();
            $scope.newlink.status = false;
            $scope.searchBy = '';
            $scope.menu.active(next.originalPath);

        });

        /*Popup model functions*/
        $scope.popup = {
            edit: {
                close: function () {
                    $location.path('/links').replace();
                }
            }
        };
        /*Some elements should change with resolution so it's nice to include them*/
        var bootstrap_resolution = {
            resl: document.body.innerWidth,
            onchange: function () {}
            //1200
            //768
            //480
            //320
        };

        /*Stats chart settings*/

        $scope.datasetOverride = [{
            yAxisID: 'y-axis-1'
        }, {
            yAxisID: 'y-axis-2'
            }];

        $scope.options = {
            scales: {
                yAxes: [
                    {
                        id: 'y-axis-1',
                        type: 'linear',
                        display: true,
                        position: 'left'
                    }
                        ]
            }
        };

        /*Custom Scrollbar Config*/
        $scope.config = {
            autoHideScrollbar: true,
            theme: 'minimal',
            advanced: {
                updateOnContentResize: true
            },
            scrollInertia: 0,
            axis: 'y'
        };
    }]);

}());
<commit_msg>Fix refresh and uploader<commit_after>(function () {
    var app = angular.module('mainApp', ['ngRoute', 'config', 'ngScrollbars', 'services', 'directives', 'chart.js', 'angularFileUpload']);
    app.controller('mainCtrl', ['$scope', 'restful', '$location', '$route', '$routeParams', '$interval', 'FileUploader', '$http', '$document', '$filter', function ($scope, restful, $location, $route, $routeParams, $interval, FileUploader, $http, $document, $filter) {

        $scope.todayDateTime = new Date;

        /*Append loading page druing data fetching*/
        var loadingPage = {
            ready: function () {
                angular.element('.loading .text').innerHTML = 'Fetching data...';
                var interval = $interval(function () {
                    if ($http.pendingRequests < 1) {
                        angular.element('.loading').addClass('hidden');
                        $document.find('body').css('overflow', 'auto');
                        $interval.cancel(interval);
                    }
                }, 1000);
            }
        };
        /*Hide it when content is loaded*/
        $scope.$on('$viewContentLoaded', function () {
            loadingPage.ready();
        });

        /*Change route to dashboard*/
        $scope.redirectToDash = function () {
            $location.path('/dashboard');
        };

        /*Limits for lists*/
        $scope.limit = {
            users: 5,
            group: 3,
            log: 5,
            components: 5
        };

        /*Menu elements*/
        $scope.menu = {
            status: true,
            hide: function () {
                this.status = !this.status;
            },
            active: function (url) {
                if (url.indexOf(':siteID') === -1) {
                    var arr = angular.element('#navigation a').removeClass('active-li');
                    for (var i = 0; i < arr.length; i++) {
                        if (arr[i].href.indexOf(url) !== -1)
                            var active = arr[i];
                    }
                    active.className += ' active-li';
                }
            }
        };
        /*Init stats objects*/
        $scope.stats = {
            apps: {},
            users: {},
            components: {},
            logs: {}
        }

        /*Filter apps by params in a route*/
        $scope.filterParams = {
            manage: function () {
                if ($routeParams.siteID)
                    return parseInt($routeParams.siteID);
                else return '';
            }
        };

        var generateWeekStats = function (array) {
            var today = new Date;
            var week = [];
            week.push($filter('date')(today, 'MM/dd/yyyy'));
            var count = [0, 0, 0, 0, 0];
            for (var i = 0; i < 4; i++) {
                today.setDate(today.getDate() - 1);
                week.push($filter('date')(today, 'MM/dd/yyyy'));
            }
            week.reverse();
            for (var i = 0; i < array.length; i++) {
                var day = $filter('date')(new Date(array[i].date.replace(' ', 'T')), 'MM/dd/yyyy');
                var foundAt = week.indexOf(day);
                if (foundAt !== -1)
                    count[foundAt] += 1;
            }
            return {
                week: week,
                count: count
            }
        }

        var generateTwoDaysStats = function (array) {
            var date = new Date;
            var today = $filter('date')(date, 'MM/dd/yyyy')
            var yesterday = $filter('date')(date.setDate(date.getDate() - 1), 'MM/dd/yyyy')
            var days = [today, yesterday];
            var count = [0, 0];
            for (var i = 0; i < array.length; i++) {
                var day = $filter('date')(new Date(array[i].date.replace(' ', 'T')), 'MM/dd/yyyy');
                var foundAt = days.indexOf(day);
                if (foundAt !== -1)
                    count[foundAt] += 1;
            }
            return {
                count: count.reverse()
            }
        };

        var generateTripleDataStats = function (array) {
            var date = new Date;
            var today = $filter('date')(date, 'MM/dd/yyyy');
            var count = {
                'drop': 0,
                'edit': 0
            };
            for (var i = 0; i < array.length; i++) {
                var day = $filter('date')(new Date(array[i].date.replace(' ', 'T')), 'MM/dd/yyyy');
                if (today == day)
                    if (array[i].content.indexOf('updated') !== -1)
                        count.edit += 1;
                    else
                        count.drop += 1;
            }
            return count;
        }

        /*Get data model*/
        var update = {
            me: function () {
                restful.get('me/user').then(function (response) {
                    $scope.current_user = response['objects'][0];
                });
            },
            apps: function () {
                restful.get('app').then(function (response) {
                    var apps = $scope.apps = response['objects'];
                    $scope.stats.apps = {
                        labels: generateWeekStats(apps).week,
                        series: ['Applications created'],
                        data: [generateWeekStats(apps).count]
                    }
                });
            },
            users: function () {
                restful.get('user').then(function (response) {
                    var users = $scope.users = response['objects'];
                    $scope.stats.users = {
                        labels: ['Yesterday', 'Today'],
                        series: ['Registered'],
                        data: [generateTwoDaysStats(users).count]
                    }
                });
            },
            groups: function () {
                restful.get('group').then(function (response) {
                    $scope.groups = response['objects'];
                });
            },
            logs: function () {
                restful.get('log').then(function (response) {
                    var logs = $scope.logs = response['objects'];
                    $scope.logs.todaysAct = generateTripleDataStats(logs).edit + generateTripleDataStats(logs).drop;
                    $scope.stats.logs = {
                        labels: ['Activity'],
                        series: ['Updates', 'Deletions'],
                        data: [
                    [generateTripleDataStats(logs).edit],
                    [generateTripleDataStats(logs).drop]
                  ]
                    }
                });
            },
            notes: function () {
                restful.get('note').then(function (response) {
                    $scope.notes = response['objects'];
                });
            },
            components: function () {
                restful.get('component').then(function (response) {
                    $scope.components = response['objects'];
                });
            },
            all: function () {
                this.me();
                this.apps();
                this.users();
                this.groups();
                this.logs();
                this.notes();
                this.components();
            }
        };
        update.all();
        /*Dashboard clock*/
        $scope.clockDate = {
            setup: new Date(),
            time: function () {
                if (this.setup.getMinutes() < 10)
                    return this.setup.getHours() > 9 ? this.setup.getHours() + ':0' + this.setup.getMinutes() : '0' + this.setup.getHours() + ':0' + this.setup.getMinutes();
                else
                    return this.setup.getHours() > 9 ? this.setup.getHours() + ':' + this.setup.getMinutes() : '0' + this.setup.getHours() + ':' + this.setup.getMinutes();
            },
            date: function () {
                return this.setup.getTime();
            },
            update: function () {
                this.setup = new Date();
            }
        };

        /*Refresh clock every 30s*/
        $interval(function () {
            $scope.clockDate.update()
        }, 30000)

        /*Applications methods*/
        $scope.newlink = {
            uploader: new FileUploader({
                url: 'api/upload/img',
                formData: [],
                removeAfterUpload: true,
                withCredentials: true,
                queueLimit: 1
            }),
            name: '',
            address: '',
            desc: '',
            img_link: '',
            order_id: '',
            beta: null,
            manageFill: function (name, link, desc, img_link, order_id, beta) {
                this.name = name;
                this.address = link;
                this.desc = desc;
                this.img_link = img_link;
                this.order_id = order_id;
                this.beta = beta;
            },
            add: function () {
                var img_link = $scope.clockDate.date();
                this.uploader.onBeforeUploadItem = function (item) {
                    item.formData.push({
                        filename: img_link
                    });
                }
                this.uploader.uploadItem(0);
                this.uploader.onSuccessItem = (item, response, status, headers) => {
                    var post_object = {
                        name: this.name,
                        link: this.address,
                        desc: this.desc,
                        creator_id: $scope.current_user.id,
                        img_link: img_link,
                    }
                    restful.post('app', post_object).then(function () {
                        update.apps();
                    });
                    this.clear();
                    this.status = true;
                    this.uploader.clearQueue();
                };
                this.uploader.onErrorItem = function (item, response, status, headers) {
                    console.log('Uplaoder: Error callback');
                    console.log(item);
                    console.log(response);
                    console.log(headers);
                };

                this.uploader.onCancelItem = function (item, response, status, headers) {
                    console.log('Uploader: Cancel callback');
                    console.log(item);
                    console.log(response);
                    console.log(headers);
                };

            },
            update: function (app_id) {
                if (this.uploader.queue.length > 0) {
                    this.img_link = new Date();
                    this.img_link = this.img_link.getTime();
                    this.uploader.onBeforeUploadItem = (item) => {
                        item.formData.push({
                            filename: this.img_link
                        });
                    }
                    this.uploader.uploadAll();
                    this.uploader.onSuccessItem = (item, response, status, headers) => {
                        console.log('Uploader: Success callback');
                        this.uploader.clearQueue();
                    }
                    this.uploader.onErrorItem = (item, response, status, headers) => {
                        //Add information for img uplaod error in DOM (html)
                        console.log('Uplaoder: Error callback');
                        console.log(item);
                        console.log(response);
                        console.log(headers);
                    };
                    this.uploader.onCancelItem = (item, response, status, headers) => {
                        //Add information for img uplaod error in DOM (html)
                        console.log('Uploader: Cancel callback');
                        console.log(item);
                        console.log(response);
                        console.log(headers);
                    };
                }
                var post_object = {
                    name: this.name,
                    link: this.address,
                    desc: this.desc,
                    img_link: this.img_link,
                    order_id: this.order_id,
                    beta: this.beta,
                }
                restful.update('app', app_id, post_object).then(function () {
                    var log_object = {
                        content: 'A link #' + app_id + ' was updated',
                        author_id: $scope.current_user.id
                    }
                    update.apps();
                    restful.post('log', log_object).then(function () {
                        update.logs();
                    });
                });
                this.clear();
                this.status = true;
                $location.path('/links').replace();
            },
            hide: function (app_id, app_status) {
                var confirmResult = confirm("Do you want to change visibility of this app?");
                if (confirmResult) {
                    var hide = {
                        status: !app_status
                    }
                    restful.update('app', app_id, hide).then(function () {
                        var log_object = {
                            content: 'A link #' + app_id + ' was updated',
                            author_id: $scope.current_user.id
                        }
                        update.apps();
                        restful.post('log', log_object).then(function () {
                            update.logs();
                        });
                    });
                }
            },
            delete: function (app_id) {
                var confirmResult = confirm("Do you want to remove this app?");
                if (confirmResult) {
                    restful.delete("app", app_id).then(function () {
                        var log_object = {
                            content: 'A link #' + app_id + ' was removed',
                            author_id: $scope.current_user.id
                        }
                        update.apps();
                        restful.post('log', log_object).then(function () {
                            update.logs();
                        });
                    });
                }
            },
            clear: function () {
                this.name = '';
                this.address = '';
                this.desc = '';
            },
            status: false
        };

        $scope.user_inf = {
            uploader: new FileUploader({
                url: 'api/upload/avatar',
                formData: []
            }),
            manageFill(username, email) {
                this.username = username;
                this.email = email;
            },
            img_update: function () {
                if (this.uploader.queue.length > 0) {
                    this.uploader.uploadAll();
                    restful.update('user', $scope.current_user.id, {
                        avatar_url: 'avatar_' + $scope.current_user.id
                    }).then((response)=> {
                        this.uploader.clearQueue();
                        window.location.reload();
                    });
                }
            },
            user_update: function () {
                if (this.password == null || this.password == this.pass_verify)
                    restful.post('auth/checkpass', {
                        pass: this.current_pass
                    }).then((response) => {
                        if (response == 'True') {
                            var user_info = {
                                username: this.username,
                                email: this.email,
                            }
                            restful.update('user', $scope.current_user.id, user_info).then((response) => {
                                update.me();
                                $scope.userFormSucces = true;
                                this.clear();
                            });
                            if (this.pass_verify != null && this.password != null && this.password == this.pass_verify)
                                restful.post('auth/changepass', {
                                    newpass: this.password
                                }).then(() => {
                                    window.location.reload();
                                })
                        } else
                            $scope.userFormWrongPass = true;
                    });
            },
            clear: function () {
                this.password = '';
                this.current_pass = '';
                this.pass_verify = '';
            }

        };

        $scope.note = {
            content: '',
            tag: '',
            add: function () {
                var post_note = {
                    content: this.content,
                    tag: this.tag,
                    owner_id: $scope.current_user.id,
                }
                restful.post('note', post_note).then(function () {
                    update.notes();
                });
                this.clear();
            },
            clear: function () {
                this.content = '';
                this.tag = '';
            }
        };

        $scope.orderArray = [1, 2, 3, 4, 5, 6, 7, 8, 9];
        /*
        If you use some variables in a couple places you probably
        want to reset them when you leave a page. That's why you should put
        those variables/functions below.
        */
        $scope.$on('$routeChangeStart', function (current, next) {
            $scope.newlink.clear();
            $scope.newlink.status = false;
            $scope.searchBy = '';
            $scope.menu.active(next.originalPath);

        });

        /*Popup model functions*/
        $scope.popup = {
            edit: {
                close: function () {
                    $location.path('/links').replace();
                }
            }
        };
        /*Some elements should change with resolution so it's nice to include them*/
        var bootstrap_resolution = {
            resl: document.body.innerWidth,
            onchange: function () {}
            //1200
            //768
            //480
            //320
        };

        /*Stats chart settings*/

        $scope.datasetOverride = [{
            yAxisID: 'y-axis-1'
        }, {
            yAxisID: 'y-axis-2'
            }];

        $scope.options = {
            scales: {
                yAxes: [
                    {
                        id: 'y-axis-1',
                        type: 'linear',
                        display: true,
                        position: 'left'
                    }
                        ]
            }
        };

        /*Custom Scrollbar Config*/
        $scope.config = {
            autoHideScrollbar: true,
            theme: 'minimal',
            advanced: {
                updateOnContentResize: true
            },
            scrollInertia: 0,
            axis: 'y'
        };
    }]);

}());
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

const bindingGraph = (function() {
  /**
   * A dictionary that maps a node's name to its identifier.
   * @type {Map<string, number>}
   */
  const nodesToNumbers = new Map();

  /**
   * A data structure DataSet
   * that contains all the nodes from the shown subgraph.
   */
  const nodes = new vis.DataSet();

  /**
   * A data structure DataSet
   * that contains all the edges from the shown subgraph.
   */
  const edges = new vis.DataSet();

  /**
   * Specific options of the binding graph.
   */
  const options = {
    physics: false,
    layout: {
      randomSeed: 42,
    }
  };

  /**
   * Determines if graph needs to be redrawn or not.
   * @type {boolean}
   */
  let needsToRedraw = true;

  /**
   * An enum that defines which nodes from the edge will be highlighted.
   * @type {Readonly<{highlightTarget: number, noHighlight: number, highlightSource: number}>}
   */
  const EdgeType = Object.freeze({'highlightSource': 1, 'highlightTarget': 2, 'noHighlight': 3});

  /**
   * Contains information that can be used by the edges constructor to change their style.
   * @param {EdgeType} type
   * @param {string} baseColor
   * @param {string} highlightColor
   */
  function EdgeStyle(type, baseColor, highlightColor) {
    this.type = type;
    this.baseColor = baseColor;
    this.highlightColor = highlightColor;
  }

  /**
   * Contains information that can be used by the nodes constructor to change their style.
   * @param {string} colour a hex string representing the background color of the node
   * @param {string} shape the shape defines what the node looks like
   */
  function NodeStyle(colour, shape) {
    this.colour = colour;
    this.shape = shape;
  }

  const GRAY_COLOR = '#c8c8c8';
  const ELLIPSE_SHAPE = 'ellipse';
  const queueWithColours = ['#92cfff', '#fbaed2', '#93d145', '#f8cc43'];

  /**
   * Retrieves the first colour from the queue and places it at the end of the queue.
   * @return {string} the first colour from the queue
   */
  function extractColor() {
    const pickedColor = queueWithColours.shift();
    queueWithColours.push(pickedColor);

    return pickedColor;
  }

  /**
   * Fills all nodes in the graph with the given color.
   * @param {string} colour the colour that will fill all nodes
   */
  function recolourAllNodes(colour) {
    const newColours = nodes.map(node => ({
      id: node.id,
      color: {
        background: colour,
        border: colour,
        highlight: colour
      }
    }));
    nodes.update(newColours);
  }

  /**
   * Associates given node with an identifier, if the node was not represented in the graph.
   *
   * @param {string} node a node to be added if it doesn't exist
   * @param {NodeStyle} style a style with a specific color and shape to be applied to the node
   * @return {number} an identifier of a given node
   */
  function addNode(node, style) {
    if (!nodesToNumbers.has(node)) {
      nodesToNumbers.set(node, nodesToNumbers.size);
    }

    const id = nodesToNumbers.get(node);
    if (nodes.get(id) === null) {
      const simpleName = node.replace(/(?:\w+\.)+(\w+)/g, '$1');
      nodes.add({
        id: id,
        label: simpleName,
        title: node,
      });
    }

    nodes.update([{
      id: id,
      shape: style.shape,
      color: {
        background: style.colour,
        border: style.colour,
        highlight: style.colour
      }
    }]);

    return id;
  }

  /**
   * Tries to remove the given node from a subgraph.
   *
   *
The operation is considered successful only if the given node does not connect
   * to any other node in the graph.
   *
   * @param {number} nodeId an identifier of a node to be removed
   * @return {boolean} a boolean flag which indicates if a node was removed or not
   */
  function tryToRemoveNode(nodeId) {
    if (getAllEdges(nodeId).length === 0) {
      nodes.remove(nodeId);
      return true;
    }

    return false;
  }

  /**
   * Retrieves all edges where the given node equals to the source or target nodes.
   *
   * @param {number} nodeId an identifier of the given node
   */
  function getAllEdges(nodeId) {
    return edges.get({
      filter: edge => edge.from === nodeId || edge.to === nodeId
    });
  }

  /**
   * Retrieves all edges where the given node equals to the source node.
   *
   * @param {number} nodeId an identifier of the given node
   */
  function getAllEdgesToDeps(nodeId) {
    return edges.get({
      filter: edge => edge.from === nodeId
    });
  }

  /**
   * Retrieves all edges where the given node equals to the target node.
   *
   * @param {number} nodeId an identifier of the given node
   */
  function getAllEdgesFromAncestors(nodeId) {
    return edges.get({
      filter: edge => edge.to === nodeId
    });
  }

  /**
   * Removes an edge from a subgraph, and if the destination node
   * becomes isolated, then removes it as well.
   *
   * @param {vis.Edge} edge an edge which will be removed
   * @param {boolean} shouldRemoveSource
   * @param {boolean} shouldRemoveTarget
   */
  function removeEdge(edge, shouldRemoveSource, shouldRemoveTarget) {
    edges.remove(edge);

    if (shouldRemoveSource) {
      tryToRemoveNode(edge.from);
    }
    if (shouldRemoveTarget) {
      tryToRemoveNode(edge.to);
    }
  }

  /**
   * Adds an edge from the source node to the target.
   *
   * @param {string} source the first node of the given edge
   * @param {string} target the second node of the given edge
   * @param {EdgeStyle} style specifies which nodes will be highlighted
   */
  function addEdge(source, target, style) {
    const sourceId = addNode(source, style.type === EdgeType.highlightSource ?
      new NodeStyle(style.highlightColor, ELLIPSE_SHAPE) : new NodeStyle(style.baseColor, ELLIPSE_SHAPE));
    const targetId = addNode(target, style.type === EdgeType.highlightTarget ?
      new NodeStyle(style.highlightColor, ELLIPSE_SHAPE) : new NodeStyle(style.baseColor, ELLIPSE_SHAPE));

    // Checks if an edge already exists.
    if (hasEdge(sourceId, targetId)) {
      return;
    }

    edges.add({from: sourceId, to: targetId, arrows: 'to', color: GRAY_COLOR});
  }

  /**
   * Checks if an edge exists in the graph or not.
   *
   * @param {number} sourceId
   * @param {number} targetId
   * @return {boolean}
   */
  function hasEdge(sourceId, targetId) {
    return edges.get({
      filter: edge => edge.from === sourceId && edge.to === targetId
    }).length !== 0;
  }

  /**
   * Checks if a node exists in the graph or not.
   *
   * @param {string} nodeName
   * @return {boolean}
   */
  function hasNode(nodeName) {
    return nodes.get({
      filter: nodes => nodes.title === nodeName
    }).length !== 0;
  }

  /**
   * If the dependencies are not currently shown, we prefer to draw them all.
   * Otherwise, we hide all children, even if they are not all were presented.
   *
   * @param {number} nodeId
   */
  function showOrHideDeps(nodeId) {
    const nodeTitle = nodes.get(nodeId).title;

    if (getAllEdgesToDeps(nodeId).length === 0) {
      queryExecutor.processQuery(['deps', nodeTitle], false);
    } else {
      bindingGraph.deleteDeps(nodeTitle);
    }
  }

  /**
   * If the ancestors are not currently shown, we prefer to draw them all.
   * Otherwise, we hide all parents, even if they are not all were presented.
   *
   * @param {number} nodeId
   */
  function showOrHideAncestors(nodeId) {
    const nodeTitle = nodes.get(nodeId).title;

    if (getAllEdgesFromAncestors(nodeId).length === 0) {
      queryExecutor.processQuery(['rdeps', nodeTitle], false);
    } else {
      bindingGraph.deleteAncestors(nodeTitle);
    }
  }

  /**
   * Supports right-click and left-click events in the network object.
   *
   * @param {vis.Network} network
   */
  function supportEventsRecognition(network) {
    // An event for managing children nodes.
    network.on("click", function (params) {
      // Checks if any node was selected.
      if (params.nodes.length === 0) {
        return;
      }

      const nodeId = params.nodes[0];
      showOrHideDeps(nodeId);
    });

    // An event for managing parent nodes.
    network.on("oncontext", function (params) {
      // Checks if any node was selected.
      const selectedNode = this.getNodeAt(params.pointer.DOM);
      if (selectedNode === undefined) {
        return;
      }

      showOrHideAncestors(selectedNode);
      this.selectNodes([selectedNode]);
    });
  }

  /**
   * Takes a string representing a path where two adjacent nodes are connected by an edge,
   * and adds all these edges to the subgraph.
   *
   *
Given path is separated with ' -> ' arrow.
   *
   *
If the path is invalid and contains less than two nodes, the method does nothing.
   *
   * @param {string} path
   * @param {string} baseColor
   * @param {string} highlightColor
   */
  function addPath(path, baseColor, highlightColor) {
    const nodesInPath = path.split( ' -> ');
    if (nodesInPath.length < 2) {
      return;
    }

    addEdge(nodesInPath[0], nodesInPath[1], new EdgeStyle(EdgeType.highlightSource, baseColor, highlightColor));
    for (let index = 1; index + 2 < nodesInPath.length; ++index) {
      addEdge(nodesInPath[index], nodesInPath[index + 1], new EdgeStyle(EdgeType.noHighlight, baseColor, highlightColor));
    }
    addEdge(nodesInPath[nodesInPath.length - 2], nodesInPath[nodesInPath.length - 1],
      new EdgeStyle(EdgeType.highlightTarget, baseColor, highlightColor));
  }

  return {

    /**
     * Takes an array with paths and draws all of them.
     *
     *
All given paths are separated with ' -> ' arrow.
     *
     * @param {string[]} paths an array with valid paths between nodes in the graph
     */
    addPaths: function (paths) {
      recolourAllNodes(GRAY_COLOR);

      const baseColor = extractColor();
      const highlightColor = extractColor();
      for (const path of paths) {
        addPath(path, baseColor, highlightColor);
      }
    },

    /**
     * Adds all source node's dependencies to the subgraph.
     * @param {string} source
     * @param {string[]} deps
     */
    addDeps: function (source, deps) {
      if (deps.length === 0) {
        return;
      }

      recolourAllNodes(GRAY_COLOR);

      const baseColor = extractColor();
      const highlightColor = extractColor();
      for (const childNode of deps) {
        addEdge(source, childNode, new EdgeStyle(EdgeType.highlightSource, baseColor, highlightColor));
      }
    },

    /**
     * Adds all nodes which depend on the source node.
     *
     * @param {string} source
     * @param {string[]} ancestors
     */
    addAncestors: function (source, ancestors) {
      if (ancestors.length === 0) {
        return;
      }

      recolourAllNodes(GRAY_COLOR);

      const baseColor = extractColor();
      const highlightColor = extractColor();
      for (const ancestorNode of ancestors) {
        addEdge(ancestorNode, source, new EdgeStyle(EdgeType.highlightTarget, baseColor, highlightColor));
      }
    },

    /**
     * Adds a new node to the graph if it doesn't exist.
     *
     * @param {string} nodeName
     */
    addNode: function (nodeName) {
      if (hasNode(nodeName)) {
        return;
      }

      recolourAllNodes(GRAY_COLOR);

      addNode(nodeName, new NodeStyle(extractColor(), ELLIPSE_SHAPE));
    },

    /**
     * Removes all edges with the given source node from the subgraph.
     *
     * @param {string} node a source node which dependencies will be removed
     */
    deleteDeps: function (node) {
      // Retrieves all edges having a start node with value `node`.
      const edgesToBeRemoved = getAllEdgesToDeps(nodesToNumbers.get(node));

      for (const edge of edgesToBeRemoved) {
        removeEdge(edge, /*shouldRemoveSource =*/ false, /*shouldRemoveTarget =*/ true);
      }
    },

    /**
     * Removes all edges with the given target node from the subgraph.
     *
     * @param {string} node a target node which parents will be removed
     */
    deleteAncestors: function (node) {
      const edgesToBeRemoved = getAllEdgesFromAncestors(nodesToNumbers.get(node));

      for (const edge of edgesToBeRemoved) {
        removeEdge(edge, /*shouldRemoveSource =*/ true, /*shouldRemoveTarget =*/ false);
      }
    },

    /**
     * Clears all data from the graph.
     */
    clear: function () {
      nodesToNumbers.clear();
      nodes.clear();
      edges.clear();
    },

    /**
     * Returns a DataSet
     * with all edges from the subgraph.
     *
     * @return {vis.DataSet}
     */
    getEdges: function () {
      return edges;
    },

    /**
     * Returns a DataSet
     * with all nodes from the subgraph.
     *
     * @return {vis.DataSet}
     */
    getNodes: function () {
      return nodes;
    },

    /**
     * Toggles physics switcher.
     *
     *
Disables physics if enabled and vice versa.
     *
     * @return {boolean} a flag that indicates if physics is enabled or not
     */
    togglePhysics: function() {
      options.physics = !options.physics;
      needsToRedraw = true;
      return options.physics;
    },

    /**
     * Draws the entire graph and sets events listeners.
     */
    draw: function() {
      if (!needsToRedraw) {
        return;
      }

      const container = document.getElementById('binding-graph');
      const data = {
        nodes: nodes,
        edges: edges
      };

      network = new vis.Network(container, data, options);
      supportEventsRecognition(network);

      // With physics enabled, we don't need to draw the graph again, because the nodes are located by gravity.
      // Otherwise, all the nodes will appear in the initial position and overlap, so we need to redraw the graph.
      if (options.physics) {
        needsToRedraw = false;
      }
    },
  };
})();
<commit_msg>Removed additional recolouring to the grey<commit_after>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

const bindingGraph = (function() {
  /**
   * A dictionary that maps a node's name to its identifier.
   * @type {Map<string, number>}
   */
  const nodesToNumbers = new Map();

  /**
   * A data structure DataSet
   * that contains all the nodes from the shown subgraph.
   */
  const nodes = new vis.DataSet();

  /**
   * A data structure DataSet
   * that contains all the edges from the shown subgraph.
   */
  const edges = new vis.DataSet();

  /**
   * Specific options of the binding graph.
   */
  const options = {
    physics: false,
    layout: {
      randomSeed: 42,
    }
  };

  /**
   * Determines if graph needs to be redrawn or not.
   * @type {boolean}
   */
  let needsToRedraw = true;

  /**
   * An enum that defines which nodes from the edge will be highlighted.
   * @type {Readonly<{highlightTarget: number, noHighlight: number, highlightSource: number}>}
   */
  const EdgeType = Object.freeze({'highlightSource': 1, 'highlightTarget': 2, 'noHighlight': 3});

  /**
   * Contains information that can be used by the edges constructor to change their style.
   * @param {EdgeType} type
   * @param {string} baseColor
   * @param {string} highlightColor
   */
  function EdgeStyle(type, baseColor, highlightColor) {
    this.type = type;
    this.baseColor = baseColor;
    this.highlightColor = highlightColor;
  }

  /**
   * Contains information that can be used by the nodes constructor to change their style.
   * @param {string} colour a hex string representing the background color of the node
   * @param {string} shape the shape defines what the node looks like
   */
  function NodeStyle(colour, shape) {
    this.colour = colour;
    this.shape = shape;
  }

  const GRAY_COLOR = '#c8c8c8';
  const ELLIPSE_SHAPE = 'ellipse';
  const queueWithColours = ['#92cfff', '#fbaed2', '#93d145', '#f8cc43'];

  /**
   * Retrieves the first colour from the queue and places it at the end of the queue.
   * @return {string} the first colour from the queue
   */
  function extractColor() {
    const pickedColor = queueWithColours.shift();
    queueWithColours.push(pickedColor);

    return pickedColor;
  }

  /**
   * Fills all nodes in the graph with the given color.
   * @param {string} colour the colour that will fill all nodes
   */
  function recolourAllNodes(colour) {
    const newColours = nodes.map(node => ({
      id: node.id,
      color: {
        background: colour,
        border: colour,
        highlight: colour
      }
    }));
    nodes.update(newColours);
  }

  /**
   * Associates given node with an identifier, if the node was not represented in the graph.
   *
   * @param {string} node a node to be added if it doesn't exist
   * @param {NodeStyle} style a style with a specific color and shape to be applied to the node
   * @return {number} an identifier of a given node
   */
  function addNode(node, style) {
    if (!nodesToNumbers.has(node)) {
      nodesToNumbers.set(node, nodesToNumbers.size);
    }

    const id = nodesToNumbers.get(node);
    if (nodes.get(id) === null) {
      const simpleName = node.replace(/(?:\w+\.)+(\w+)/g, '$1');
      nodes.add({
        id: id,
        label: simpleName,
        title: node,
      });
    }

    nodes.update([{
      id: id,
      shape: style.shape,
      color: {
        background: style.colour,
        border: style.colour,
        highlight: style.colour
      }
    }]);

    return id;
  }

  /**
   * Tries to remove the given node from a subgraph.
   *
   *
The operation is considered successful only if the given node does not connect
   * to any other node in the graph.
   *
   * @param {number} nodeId an identifier of a node to be removed
   * @return {boolean} a boolean flag which indicates if a node was removed or not
   */
  function tryToRemoveNode(nodeId) {
    if (getAllEdges(nodeId).length === 0) {
      nodes.remove(nodeId);
      return true;
    }

    return false;
  }

  /**
   * Retrieves all edges where the given node equals to the source or target nodes.
   *
   * @param {number} nodeId an identifier of the given node
   */
  function getAllEdges(nodeId) {
    return edges.get({
      filter: edge => edge.from === nodeId || edge.to === nodeId
    });
  }

  /**
   * Retrieves all edges where the given node equals to the source node.
   *
   * @param {number} nodeId an identifier of the given node
   */
  function getAllEdgesToDeps(nodeId) {
    return edges.get({
      filter: edge => edge.from === nodeId
    });
  }

  /**
   * Retrieves all edges where the given node equals to the target node.
   *
   * @param {number} nodeId an identifier of the given node
   */
  function getAllEdgesFromAncestors(nodeId) {
    return edges.get({
      filter: edge => edge.to === nodeId
    });
  }

  /**
   * Removes an edge from a subgraph, and if the destination node
   * becomes isolated, then removes it as well.
   *
   * @param {vis.Edge} edge an edge which will be removed
   * @param {boolean} shouldRemoveSource
   * @param {boolean} shouldRemoveTarget
   */
  function removeEdge(edge, shouldRemoveSource, shouldRemoveTarget) {
    edges.remove(edge);

    if (shouldRemoveSource) {
      tryToRemoveNode(edge.from);
    }
    if (shouldRemoveTarget) {
      tryToRemoveNode(edge.to);
    }
  }

  /**
   * Adds an edge from the source node to the target.
   *
   * @param {string} source the first node of the given edge
   * @param {string} target the second node of the given edge
   * @param {EdgeStyle} style specifies which nodes will be highlighted
   */
  function addEdge(source, target, style) {
    const sourceId = addNode(source, style.type === EdgeType.highlightSource ?
      new NodeStyle(style.highlightColor, ELLIPSE_SHAPE) : new NodeStyle(style.baseColor, ELLIPSE_SHAPE));
    const targetId = addNode(target, style.type === EdgeType.highlightTarget ?
      new NodeStyle(style.highlightColor, ELLIPSE_SHAPE) : new NodeStyle(style.baseColor, ELLIPSE_SHAPE));

    // Checks if an edge already exists.
    if (hasEdge(sourceId, targetId)) {
      return;
    }

    edges.add({from: sourceId, to: targetId, arrows: 'to', color: GRAY_COLOR});
  }

  /**
   * Checks if an edge exists in the graph or not.
   *
   * @param {number} sourceId
   * @param {number} targetId
   * @return {boolean}
   */
  function hasEdge(sourceId, targetId) {
    return edges.get({
      filter: edge => edge.from === sourceId && edge.to === targetId
    }).length !== 0;
  }

  /**
   * Checks if a node exists in the graph or not.
   *
   * @param {string} nodeName
   * @return {boolean}
   */
  function hasNode(nodeName) {
    return nodes.get({
      filter: nodes => nodes.title === nodeName
    }).length !== 0;
  }

  /**
   * If the dependencies are not currently shown, we prefer to draw them all.
   * Otherwise, we hide all children, even if they are not all were presented.
   *
   * @param {number} nodeId
   */
  function showOrHideDeps(nodeId) {
    const nodeTitle = nodes.get(nodeId).title;

    if (getAllEdgesToDeps(nodeId).length === 0) {
      queryExecutor.processQuery(['deps', nodeTitle], false);
    } else {
      bindingGraph.deleteDeps(nodeTitle);
    }
  }

  /**
   * If the ancestors are not currently shown, we prefer to draw them all.
   * Otherwise, we hide all parents, even if they are not all were presented.
   *
   * @param {number} nodeId
   */
  function showOrHideAncestors(nodeId) {
    const nodeTitle = nodes.get(nodeId).title;

    if (getAllEdgesFromAncestors(nodeId).length === 0) {
      queryExecutor.processQuery(['rdeps', nodeTitle], false);
    } else {
      bindingGraph.deleteAncestors(nodeTitle);
    }
  }

  /**
   * Supports right-click and left-click events in the network object.
   *
   * @param {vis.Network} network
   */
  function supportEventsRecognition(network) {
    // An event for managing children nodes.
    network.on("click", function (params) {
      // Checks if any node was selected.
      if (params.nodes.length === 0) {
        return;
      }

      const nodeId = params.nodes[0];
      showOrHideDeps(nodeId);
    });

    // An event for managing parent nodes.
    network.on("oncontext", function (params) {
      // Checks if any node was selected.
      const selectedNode = this.getNodeAt(params.pointer.DOM);
      if (selectedNode === undefined) {
        return;
      }

      showOrHideAncestors(selectedNode);
      this.selectNodes([selectedNode]);
    });
  }

  /**
   * Takes a string representing a path where two adjacent nodes are connected by an edge,
   * and adds all these edges to the subgraph.
   *
   *
Given path is separated with ' -> ' arrow.
   *
   *
If the path is invalid and contains less than two nodes, the method does nothing.
   *
   * @param {string} path
   * @param {string} baseColor
   * @param {string} highlightColor
   */
  function addPath(path, baseColor, highlightColor) {
    const nodesInPath = path.split( ' -> ');
    if (nodesInPath.length < 2) {
      return;
    }

    addEdge(nodesInPath[0], nodesInPath[1], new EdgeStyle(EdgeType.highlightSource, baseColor, highlightColor));
    for (let index = 1; index + 2 < nodesInPath.length; ++index) {
      addEdge(nodesInPath[index], nodesInPath[index + 1], new EdgeStyle(EdgeType.noHighlight, baseColor, highlightColor));
    }
    addEdge(nodesInPath[nodesInPath.length - 2], nodesInPath[nodesInPath.length - 1],
      new EdgeStyle(EdgeType.highlightTarget, baseColor, highlightColor));
  }

  return {

    /**
     * Takes an array with paths and draws all of them.
     *
     *
All given paths are separated with ' -> ' arrow.
     *
     * @param {string[]} paths an array with valid paths between nodes in the graph
     */
    addPaths: function (paths) {
      const baseColor = extractColor();
      const highlightColor = extractColor();
      for (const path of paths) {
        addPath(path, baseColor, highlightColor);
      }
    },

    /**
     * Adds all source node's dependencies to the subgraph.
     * @param {string} source
     * @param {string[]} deps
     */
    addDeps: function (source, deps) {
      if (deps.length === 0) {
        return;
      }

      recolourAllNodes(GRAY_COLOR);

      const baseColor = extractColor();
      const highlightColor = extractColor();
      for (const childNode of deps) {
        addEdge(source, childNode, new EdgeStyle(EdgeType.highlightSource, baseColor, highlightColor));
      }
    },

    /**
     * Adds all nodes which depend on the source node.
     *
     * @param {string} source
     * @param {string[]} ancestors
     */
    addAncestors: function (source, ancestors) {
      if (ancestors.length === 0) {
        return;
      }

      recolourAllNodes(GRAY_COLOR);

      const baseColor = extractColor();
      const highlightColor = extractColor();
      for (const ancestorNode of ancestors) {
        addEdge(ancestorNode, source, new EdgeStyle(EdgeType.highlightTarget, baseColor, highlightColor));
      }
    },

    /**
     * Adds a new node to the graph if it doesn't exist.
     *
     * @param {string} nodeName
     */
    addNode: function (nodeName) {
      if (hasNode(nodeName)) {
        return;
      }

      recolourAllNodes(GRAY_COLOR);

      addNode(nodeName, new NodeStyle(extractColor(), ELLIPSE_SHAPE));
    },

    /**
     * Removes all edges with the given source node from the subgraph.
     *
     * @param {string} node a source node which dependencies will be removed
     */
    deleteDeps: function (node) {
      // Retrieves all edges having a start node with value `node`.
      const edgesToBeRemoved = getAllEdgesToDeps(nodesToNumbers.get(node));

      for (const edge of edgesToBeRemoved) {
        removeEdge(edge, /*shouldRemoveSource =*/ false, /*shouldRemoveTarget =*/ true);
      }
    },

    /**
     * Removes all edges with the given target node from the subgraph.
     *
     * @param {string} node a target node which parents will be removed
     */
    deleteAncestors: function (node) {
      const edgesToBeRemoved = getAllEdgesFromAncestors(nodesToNumbers.get(node));

      for (const edge of edgesToBeRemoved) {
        removeEdge(edge, /*shouldRemoveSource =*/ true, /*shouldRemoveTarget =*/ false);
      }
    },

    /**
     * Clears all data from the graph.
     */
    clear: function () {
      nodesToNumbers.clear();
      nodes.clear();
      edges.clear();
    },

    /**
     * Returns a DataSet
     * with all edges from the subgraph.
     *
     * @return {vis.DataSet}
     */
    getEdges: function () {
      return edges;
    },

    /**
     * Returns a DataSet
     * with all nodes from the subgraph.
     *
     * @return {vis.DataSet}
     */
    getNodes: function () {
      return nodes;
    },

    /**
     * Toggles physics switcher.
     *
     *
Disables physics if enabled and vice versa.
     *
     * @return {boolean} a flag that indicates if physics is enabled or not
     */
    togglePhysics: function() {
      options.physics = !options.physics;
      needsToRedraw = true;
      return options.physics;
    },

    /**
     * Draws the entire graph and sets events listeners.
     */
    draw: function() {
      if (!needsToRedraw) {
        return;
      }

      const container = document.getElementById('binding-graph');
      const data = {
        nodes: nodes,
        edges: edges
      };

      network = new vis.Network(container, data, options);
      supportEventsRecognition(network);

      // With physics enabled, we don't need to draw the graph again, because the nodes are located by gravity.
      // Otherwise, all the nodes will appear in the initial position and overlap, so we need to redraw the graph.
      if (options.physics) {
        needsToRedraw = false;
      }
    },
  };
})();
<|endoftext|>


<commit_before>/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2018 Photon Storm Ltd.
 * @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
 */

var Class = require('../../../src/utils/Class');
var GetFastValue = require('../../../src/utils/object/GetFastValue');
var ImageFile = require('../../../src/loader/filetypes/ImageFile.js');
var IsPlainObject = require('../../../src/utils/object/IsPlainObject');
var JSONFile = require('../../../src/loader/filetypes/JSONFile.js');
var MultiFile = require('../../../src/loader/MultiFile.js');
var TextFile = require('../../../src/loader/filetypes/TextFile.js');

/**
 * @typedef {object} Phaser.Loader.FileTypes.SpineFileConfig
 *
 * @property {string} key - The key of the file. Must be unique within both the Loader and the Texture Manager.
 * @property {string|string[]} [jsonURL] - The absolute or relative URL to load the JSON file from. If undefined or `null` it will be set to `<key>.json`, i.e. if `key` was "alien" then the URL will be "alien.json".
 * @property {string} [atlasURL] - The absolute or relative URL to load the texture atlas data file from. If undefined or `null` it will be set to `<key>.txt`, i.e. if `key` was "alien" then the URL will be "alien.txt".
 * @property {boolean} [preMultipliedAlpha=false] - Do the textures contain pre-multiplied alpha or not?
 * @property {XHRSettingsObject} [jsonXhrSettings] - An XHR Settings configuration object for the json file. Used in replacement of the Loaders default XHR Settings.
 * @property {XHRSettingsObject} [atlasXhrSettings] - An XHR Settings configuration object for the atlas data file. Used in replacement of the Loaders default XHR Settings.
 */

/**
 * @classdesc
 * A Spine File suitable for loading by the Loader.
 *
 * These are created when you use the Phaser.Loader.LoaderPlugin#spine method and are not typically created directly.
 *
 * For documentation about what all the arguments and configuration options mean please see Phaser.Loader.LoaderPlugin#spine.
 *
 * @class SpineFile
 * @extends Phaser.Loader.MultiFile
 * @memberof Phaser.Loader.FileTypes
 * @constructor
 *
 * @param {Phaser.Loader.LoaderPlugin} loader - A reference to the Loader that is responsible for this file.
 * @param {(string|Phaser.Loader.FileTypes.SpineFileConfig)} key - The key to use for this file, or a file configuration object.
 * @param {string|string[]} [jsonURL] - The absolute or relative URL to load the JSON file from. If undefined or `null` it will be set to `<key>.json`, i.e. if `key` was "alien" then the URL will be "alien.json".
 * @param {string} [atlasURL] - The absolute or relative URL to load the texture atlas data file from. If undefined or `null` it will be set to `<key>.txt`, i.e. if `key` was "alien" then the URL will be "alien.txt".
 * @param {boolean} [preMultipliedAlpha=false] - Do the textures contain pre-multiplied alpha or not?
 * @param {XHRSettingsObject} [jsonXhrSettings] - An XHR Settings configuration object for the json file. Used in replacement of the Loaders default XHR Settings.
 * @param {XHRSettingsObject} [atlasXhrSettings] - An XHR Settings configuration object for the atlas data file. Used in replacement of the Loaders default XHR Settings.
 */
var SpineFile = new Class({

    Extends: MultiFile,

    initialize:

    function SpineFile (loader, key, jsonURL, atlasURL, preMultipliedAlpha, jsonXhrSettings, atlasXhrSettings)
    {
        var i;
        var json;
        var atlas;
        var files = [];
        var cache = loader.cacheManager.custom.spine;

        //  atlas can be an array of atlas files, not just a single one

        if (IsPlainObject(key))
        {
            var config = key;

            key = GetFastValue(config, 'key');

            json = new JSONFile(loader, {
                key: key,
                url: GetFastValue(config, 'jsonURL'),
                extension: GetFastValue(config, 'jsonExtension', 'json'),
                xhrSettings: GetFastValue(config, 'jsonXhrSettings')
            });

            atlasURL = GetFastValue(config, 'atlasURL');
            preMultipliedAlpha = GetFastValue(config, 'preMultipliedAlpha');

            if (!Array.isArray(atlasURL))
            {
                atlasURL = [ atlasURL ];
            }

            for (i = 0; i < atlasURL.length; i++)
            {
                atlas = new TextFile(loader, {
                    key: key + '_' + i,
                    url: atlasURL[i],
                    extension: GetFastValue(config, 'atlasExtension', 'atlas'),
                    xhrSettings: GetFastValue(config, 'atlasXhrSettings')
                });

                atlas.cache = cache;

                files.push(atlas);
            }
        }
        else
        {
            json = new JSONFile(loader, key, jsonURL, jsonXhrSettings);

            if (!Array.isArray(atlasURL))
            {
                atlasURL = [ atlasURL ];
            }

            for (i = 0; i < atlasURL.length; i++)
            {
                atlas = new TextFile(loader, key + '_' + i, atlasURL[i], atlasXhrSettings);
                atlas.cache = cache;

                files.push(atlas);
            }
        }

        files.unshift(json);

        MultiFile.call(this, loader, 'spine', key, files);

        this.config.preMultipliedAlpha = preMultipliedAlpha;
    },

    /**
     * Called by each File when it finishes loading.
     *
     * @method Phaser.Loader.FileTypes.SpineFile#onFileComplete
     * @since 3.19.0
     *
     * @param {Phaser.Loader.File} file - The File that has completed processing.
     */
    onFileComplete: function (file)
    {
        var index = this.files.indexOf(file);

        if (index !== -1)
        {
            this.pending--;

            if (file.type === 'text')
            {
                //  Inspect the data for the files to now load
                var content = file.data.split('\n');

                //  Extract the textures
                var textures = [];

                for (var t = 0; t < content.length; t++)
                {
                    var line = content[t];

                    if (line.trim() === '' && t < content.length - 1)
                    {
                        line = content[t + 1];

                        textures.push(line);
                    }
                }

                var config = this.config;
                var loader = this.loader;

                var currentBaseURL = loader.baseURL;
                var currentPath = loader.path;
                var currentPrefix = loader.prefix;

                var baseURL = GetFastValue(config, 'baseURL', this.baseURL);
                var path = GetFastValue(config, 'path', file.src.match(/^.*\//))[0];
                var prefix = GetFastValue(config, 'prefix', this.prefix);
                var textureXhrSettings = GetFastValue(config, 'textureXhrSettings');

                loader.setBaseURL(baseURL);
                loader.setPath(path);
                loader.setPrefix(prefix);

                for (var i = 0; i < textures.length; i++)
                {
                    var textureURL = textures[i];

                    var key = this.prefix + textureURL;

                    var image = new ImageFile(loader, key, textureURL, textureXhrSettings);

                    this.addToMultiFile(image);

                    loader.addFile(image);
                }

                //  Reset the loader settings
                loader.setBaseURL(currentBaseURL);
                loader.setPath(currentPath);
                loader.setPrefix(currentPrefix);
            }
        }
    },

    /**
     * Adds this file to its target cache upon successful loading and processing.
     *
     * @method Phaser.Loader.FileTypes.SpineFile#addToCache
     * @since 3.19.0
     */
    addToCache: function ()
    {
        if (this.isReadyToProcess())
        {
            var fileJSON = this.files[0];

            fileJSON.addToCache();

            var atlasCache;
            var atlasKey = '';
            var combinedAtlasData = '';
            var preMultipliedAlpha = (this.config.preMultipliedAlpha) ? true : false;

            for (var i = 1; i < this.files.length; i++)
            {
                var file = this.files[i];

                if (file.type === 'text')
                {
                    atlasKey = file.key.replace(/_[\d]$/, "");

                    atlasCache = file.cache;

                    combinedAtlasData = combinedAtlasData.concat(file.data);
                }
                else
                {
                    var src = file.key.trim();
                    var pos = src.indexOf('_');
                    var key = src.substr(pos + 1);

                    this.loader.textureManager.addImage(key, file.data);
                }

                file.pendingDestroy();
            }

            atlasCache.add(atlasKey, { preMultipliedAlpha: preMultipliedAlpha, data: combinedAtlasData, prefix: this.prefix });

            this.complete = true;
        }
    }

});

module.exports = SpineFile;
<commit_msg>ESLint fix<commit_after>/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2018 Photon Storm Ltd.
 * @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
 */

var Class = require('../../../src/utils/Class');
var GetFastValue = require('../../../src/utils/object/GetFastValue');
var ImageFile = require('../../../src/loader/filetypes/ImageFile.js');
var IsPlainObject = require('../../../src/utils/object/IsPlainObject');
var JSONFile = require('../../../src/loader/filetypes/JSONFile.js');
var MultiFile = require('../../../src/loader/MultiFile.js');
var TextFile = require('../../../src/loader/filetypes/TextFile.js');

/**
 * @typedef {object} Phaser.Loader.FileTypes.SpineFileConfig
 *
 * @property {string} key - The key of the file. Must be unique within both the Loader and the Texture Manager.
 * @property {string|string[]} [jsonURL] - The absolute or relative URL to load the JSON file from. If undefined or `null` it will be set to `<key>.json`, i.e. if `key` was "alien" then the URL will be "alien.json".
 * @property {string} [atlasURL] - The absolute or relative URL to load the texture atlas data file from. If undefined or `null` it will be set to `<key>.txt`, i.e. if `key` was "alien" then the URL will be "alien.txt".
 * @property {boolean} [preMultipliedAlpha=false] - Do the textures contain pre-multiplied alpha or not?
 * @property {XHRSettingsObject} [jsonXhrSettings] - An XHR Settings configuration object for the json file. Used in replacement of the Loaders default XHR Settings.
 * @property {XHRSettingsObject} [atlasXhrSettings] - An XHR Settings configuration object for the atlas data file. Used in replacement of the Loaders default XHR Settings.
 */

/**
 * @classdesc
 * A Spine File suitable for loading by the Loader.
 *
 * These are created when you use the Phaser.Loader.LoaderPlugin#spine method and are not typically created directly.
 *
 * For documentation about what all the arguments and configuration options mean please see Phaser.Loader.LoaderPlugin#spine.
 *
 * @class SpineFile
 * @extends Phaser.Loader.MultiFile
 * @memberof Phaser.Loader.FileTypes
 * @constructor
 *
 * @param {Phaser.Loader.LoaderPlugin} loader - A reference to the Loader that is responsible for this file.
 * @param {(string|Phaser.Loader.FileTypes.SpineFileConfig)} key - The key to use for this file, or a file configuration object.
 * @param {string|string[]} [jsonURL] - The absolute or relative URL to load the JSON file from. If undefined or `null` it will be set to `<key>.json`, i.e. if `key` was "alien" then the URL will be "alien.json".
 * @param {string} [atlasURL] - The absolute or relative URL to load the texture atlas data file from. If undefined or `null` it will be set to `<key>.txt`, i.e. if `key` was "alien" then the URL will be "alien.txt".
 * @param {boolean} [preMultipliedAlpha=false] - Do the textures contain pre-multiplied alpha or not?
 * @param {XHRSettingsObject} [jsonXhrSettings] - An XHR Settings configuration object for the json file. Used in replacement of the Loaders default XHR Settings.
 * @param {XHRSettingsObject} [atlasXhrSettings] - An XHR Settings configuration object for the atlas data file. Used in replacement of the Loaders default XHR Settings.
 */
var SpineFile = new Class({

    Extends: MultiFile,

    initialize:

    function SpineFile (loader, key, jsonURL, atlasURL, preMultipliedAlpha, jsonXhrSettings, atlasXhrSettings)
    {
        var i;
        var json;
        var atlas;
        var files = [];
        var cache = loader.cacheManager.custom.spine;

        //  atlas can be an array of atlas files, not just a single one

        if (IsPlainObject(key))
        {
            var config = key;

            key = GetFastValue(config, 'key');

            json = new JSONFile(loader, {
                key: key,
                url: GetFastValue(config, 'jsonURL'),
                extension: GetFastValue(config, 'jsonExtension', 'json'),
                xhrSettings: GetFastValue(config, 'jsonXhrSettings')
            });

            atlasURL = GetFastValue(config, 'atlasURL');
            preMultipliedAlpha = GetFastValue(config, 'preMultipliedAlpha');

            if (!Array.isArray(atlasURL))
            {
                atlasURL = [ atlasURL ];
            }

            for (i = 0; i < atlasURL.length; i++)
            {
                atlas = new TextFile(loader, {
                    key: key + '_' + i,
                    url: atlasURL[i],
                    extension: GetFastValue(config, 'atlasExtension', 'atlas'),
                    xhrSettings: GetFastValue(config, 'atlasXhrSettings')
                });

                atlas.cache = cache;

                files.push(atlas);
            }
        }
        else
        {
            json = new JSONFile(loader, key, jsonURL, jsonXhrSettings);

            if (!Array.isArray(atlasURL))
            {
                atlasURL = [ atlasURL ];
            }

            for (i = 0; i < atlasURL.length; i++)
            {
                atlas = new TextFile(loader, key + '_' + i, atlasURL[i], atlasXhrSettings);
                atlas.cache = cache;

                files.push(atlas);
            }
        }

        files.unshift(json);

        MultiFile.call(this, loader, 'spine', key, files);

        this.config.preMultipliedAlpha = preMultipliedAlpha;
    },

    /**
     * Called by each File when it finishes loading.
     *
     * @method Phaser.Loader.FileTypes.SpineFile#onFileComplete
     * @since 3.19.0
     *
     * @param {Phaser.Loader.File} file - The File that has completed processing.
     */
    onFileComplete: function (file)
    {
        var index = this.files.indexOf(file);

        if (index !== -1)
        {
            this.pending--;

            if (file.type === 'text')
            {
                //  Inspect the data for the files to now load
                var content = file.data.split('\n');

                //  Extract the textures
                var textures = [];

                for (var t = 0; t < content.length; t++)
                {
                    var line = content[t];

                    if (line.trim() === '' && t < content.length - 1)
                    {
                        line = content[t + 1];

                        textures.push(line);
                    }
                }

                var config = this.config;
                var loader = this.loader;

                var currentBaseURL = loader.baseURL;
                var currentPath = loader.path;
                var currentPrefix = loader.prefix;

                var baseURL = GetFastValue(config, 'baseURL', this.baseURL);
                var path = GetFastValue(config, 'path', file.src.match(/^.*\//))[0];
                var prefix = GetFastValue(config, 'prefix', this.prefix);
                var textureXhrSettings = GetFastValue(config, 'textureXhrSettings');

                loader.setBaseURL(baseURL);
                loader.setPath(path);
                loader.setPrefix(prefix);

                for (var i = 0; i < textures.length; i++)
                {
                    var textureURL = textures[i];

                    var key = this.prefix + textureURL;

                    var image = new ImageFile(loader, key, textureURL, textureXhrSettings);

                    this.addToMultiFile(image);

                    loader.addFile(image);
                }

                //  Reset the loader settings
                loader.setBaseURL(currentBaseURL);
                loader.setPath(currentPath);
                loader.setPrefix(currentPrefix);
            }
        }
    },

    /**
     * Adds this file to its target cache upon successful loading and processing.
     *
     * @method Phaser.Loader.FileTypes.SpineFile#addToCache
     * @since 3.19.0
     */
    addToCache: function ()
    {
        if (this.isReadyToProcess())
        {
            var fileJSON = this.files[0];

            fileJSON.addToCache();

            var atlasCache;
            var atlasKey = '';
            var combinedAtlasData = '';
            var preMultipliedAlpha = (this.config.preMultipliedAlpha) ? true : false;

            for (var i = 1; i < this.files.length; i++)
            {
                var file = this.files[i];

                if (file.type === 'text')
                {
                    atlasKey = file.key.replace(/_[\d]$/, '');

                    atlasCache = file.cache;

                    combinedAtlasData = combinedAtlasData.concat(file.data);
                }
                else
                {
                    var src = file.key.trim();
                    var pos = src.indexOf('_');
                    var key = src.substr(pos + 1);

                    this.loader.textureManager.addImage(key, file.data);
                }

                file.pendingDestroy();
            }

            atlasCache.add(atlasKey, { preMultipliedAlpha: preMultipliedAlpha, data: combinedAtlasData, prefix: this.prefix });

            this.complete = true;
        }
    }

});

module.exports = SpineFile;
<|endoftext|>


<commit_before><commit_msg>Ensure argument types match.<commit_after><|endoftext|>