<commit_before>(function() {
    'use strict';

    // User Service based on https://bitbucket.org/david.antaramian/so-21662778-spa-authentication-example
    angular.module('tubular.services', ['ui.bootstrap'])
        .service('tubularHttp', [
            '$http', '$timeout', '$q', '$cacheFactory', '$cookieStore', function tubularHttp($http, $timeout, $q, $cacheFactory, $cookieStore) {
                function isAuthenticationExpired(expirationDate) {
                    var now = new Date();
                    expirationDate = new Date(expirationDate);
                    if (expirationDate - now > 0) {
                        return false;
                    } else {
                        return true;
                    }
                }

                function saveData() {
                    removeData();
                    $cookieStore.put('auth_data', me.userData);
                }

                function removeData() {
                    $cookieStore.remove('auth_data');
                }

                function retrieveSavedData() {
                    var savedData = $cookieStore.get('auth_data');
                    if (typeof savedData === 'undefined') {
                        throw new Exception('No authentication data exists');
                    } else if (isAuthenticationExpired(savedData.expirationDate)) {
                        throw new Exception('Authentication token has already expired');
                    } else {
                        me.userData = savedData;
                        setHttpAuthHeader();
                    }
                }

                function clearUserData() {
                    me.userData.isAuthenticated = false;
                    me.userData.username = '';
                    me.userData.bearerToken = '';
                    me.userData.expirationDate = null;
                }

                function setHttpAuthHeader() {
                    $http.defaults.headers.common.Authorization = 'Bearer ' + me.userData.bearerToken;
                }

                var me = this;
                me.userData = {
                    isAuthenticated: false,
                    username: '',
                    bearerToken: '',
                    expirationDate: null,
                };

                me.cache = $cacheFactory('tubularHttpCache');
                me.useCache = true;

                me.isAuthenticated = function() {
                    if (me.userData.isAuthenticated && !isAuthenticationExpired(me.userData.expirationDate)) {
                        return true;
                    } else {
                        try {
                            retrieveSavedData();
                        } catch (e) {
                            return false;
                        }
                        return true;
                    }
                };

                me.removeAuthentication = function() {
                    removeData();
                    clearUserData();
                    $http.defaults.headers.common.Authorization = null;
                };

                me.authenticate = function(username, password, successCallback, errorCallback, persistData) {
                    this.removeAuthentication();
                    var config = {
                        method: 'POST',
                        url: '/api/token',
                        headers: {
                            'Content-Type': 'application/x-www-form-urlencoded',
                        },
                        data: 'grant_type=password&username=' + username + '&password=' + password,
                    };

                    $http(config)
                        .success(function(data) {
                            me.userData.isAuthenticated = true;
                            me.userData.username = data.userName;
                            me.userData.bearerToken = data.access_token;
                            me.userData.expirationDate = new Date(data['.expires']);
                            setHttpAuthHeader();
                            if (persistData === true) {
                                saveData();
                            }
                            if (typeof successCallback === 'function') {
                                successCallback();
                            }
                        })
                        .error(function(data) {
                            if (typeof errorCallback === 'function') {
                                if (data.error_description) {
                                    errorCallback(data.error_description);
                                } else {
                                    errorCallback('Unable to contact server; please, try again later.');
                                }
                            }
                        });
                };

                me.saveDataAsync = function(model, request) {
                    var clone = angular.copy(model);
                    var originalClone = angular.copy(model.$original);

                    delete clone.$isEditing;
                    delete clone.$hasChanges;
                    delete clone.$selected;
                    delete clone.$original;
                    delete clone.$state;
                    delete clone.$valid;

                    request.data = {
                        Old: originalClone,
                        New: clone
                    };

                    var dataRequest = me.retrieveDataAsync(request);

                    dataRequest.promise.then(function(data) {
                        model.$hasChanges = false;
                        model.resetOriginal();
                        return data;
                    });

                    return dataRequest;
                };

                me.getExpirationDate = function() {
                    var date = new Date();
                    var minutes = 5;
                    return new Date(date.getTime() + minutes * 60000);
                }

                me.checksum = function(obj) {
                    var keys = Object.keys(obj).sort();
                    var output = [], prop;
                    for (var i = 0; i < keys.length; i++) {
                        prop = keys[i];
                        output.push(prop);
                        output.push(obj[prop]);
                    }
                    return JSON.stringify(output);
                };

                me.retrieveDataAsync = function(request) {
                    var checksum = me.checksum(request);

                    var canceller = $q.defer();

                    var cancel = function(reason) {
                        console.error(reason);
                        canceller.resolve(reason);
                    };

                    if ((request.requestMethod == 'GET' || request.requestMethod == 'POST') && me.useCache) {
                        var data = me.cache.get(checksum);

                        if (angular.isDefined(data) && data.Expiration.getTime() > new Date().getTime()) {
                            return {
                                promise: $q(function(resolve, reject) {
                                    resolve(data.Set);
                                }),
                                cancel: cancel
                            };
                        }
                    }

                    // TODO: Check Unauthorize error and send revalidation
                    var promise = $http({
                        url: request.serverUrl,
                        method: request.requestMethod,
                        data: request.data,
                        timeout: canceller.promise
                    }).then(function(response) {
                        $timeout.cancel(timeoutHanlder);

                        if (me.useCache) {
                            me.cache.put(checksum, { Expiration: me.getExpirationDate(), Set: response.data });
                        }

                        return response.data;
                    });

                    request.timeout = request.timeout || 15000;

                    var timeoutHanlder = $timeout(function() {
                        cancel('Timed out');
                    }, request.timeout);

                    return {
                        promise: promise,
                        cancel: cancel
                    };
                };

                me.get = function(url) {
                    return me.retrieveDataAsync({
                        serverUrl: url,
                        requestMethod: 'GET',
                    });
                };

                me.delete = function (url) {
                    return me.retrieveDataAsync({
                        serverUrl: url,
                        requestMethod: 'DELETE',
                    });
                };

                me.post = function (url, data) {
                    return me.retrieveDataAsync({
                        serverUrl: url,
                        requestMethod: 'POST',
                        data: data
                    });
                };

                me.put = function (url, data) {
                    return me.retrieveDataAsync({
                        serverUrl: url,
                        requestMethod: 'PUT',
                        data: data
                    });
                };
            }
        ])
        .service('tubularOData', ['tubularHttp', function tubularOData(tubularHttp) {
            var me = this;

            me.retrieveDataAsync = function (request) {
                var params = request.data;
                var url = request.serverUrl;
                url += url.indexOf('?') > 0 ? '&' : '?';
                url += '$format=json&$inlinecount=allpages';

                url += "&$select=" + params.Columns.map(function (el) { return el.Name; }).join(',');
                url += "&$skip=" + params.Skip;
                url += "&$top=" + params.Take;

                // TODO: Search: $scope.search and filters in columns

                request.data = null;
                request.serverUrl = url;

                var response = tubularHttp.retrieveDataAsync(request);

                var promise = response.promise.then(function(data) {
                    var result = {
                        Payload: data.value,
                        CurrentPage: 1,
                        TotalPages: 1,
                        TotalRecordCount: 1,
                        FilteredRecordCount: 1
                    };

                    result.TotalRecordCount = data["odata.count"];
                    result.FilteredRecordCount = result.TotalRecordCount; // TODO
                    result.TotalPages = parseInt(result.TotalRecordCount / params.Take);
                    result.CurrentPage = 1 + ((params.Skip / result.FilteredRecordCount) * result.TotalPages);

                    return result;
                });

                return {
                    promise: promise,
                    cancel: response.cancel
                };
            };

            me.saveDataAsync = function (model, request) {
                tubularHttp.saveDataAsync(model, request);
            };

            me.get = function(url) {
                tubularHttp.get(url);
            };

            me.delete = function (url) {
                tubularHttp.delete(url);
            };

            me.post = function (url, data) {
                tubularHttp.post(url, data);
            };

            me.put = function(url, data) {
                tubularHttp.put(url, data);
            };
        }])
        .service('tubularGridPopupService', [
            '$modal', function tubularGridPopupService($modal) {
                var me = this;

                me.openDialog = function(template, model) {
                    var dialog = $modal.open({
                        templateUrl: template,
                        backdropClass: 'fullHeight',
                        controller: [
                            '$scope', function($scope) {
                                $scope.Model = model;

                                $scope.savePopup = function () {
                                    var saveResult = $scope.Model.save();

                                    if (saveResult)
                                        dialog.close();
                                };

                                $scope.closePopup = function() {
                                    $scope.Model.revertChanges();
                                    dialog.close();
                                };
                            }
                        ]
                    });
                };
            }
        ])
        .service('tubularGridExportService', function tubularGridExportService() {
            var me = this;

            me.getColumns = function(gridScope) {
                return gridScope.columns.map(function(c) { return c.Name.replace(/([a-z])([A-Z])/g, '$1 $2'); });
            };

            me.exportAllGridToCsv = function(filename, gridScope) {
                var columns = me.getColumns(gridScope);
                gridScope.getFullDataSource(function(data) {
                    me.exportToCsv(filename, columns, data);
                });

            };

            me.exportGridToCsv = function(filename, gridScope) {
                var columns = me.getColumns(gridScope);
                gridScope.currentRequest = {};
                me.exportToCsv(filename, columns, gridScope.dataSource.Payload);
                gridScope.currentRequest = null;
            };

            me.exportToCsv = function(filename, header, rows) {
                var processRow = function(row) {
                    var finalVal = '';
                    for (var j = 0; j < row.length; j++) {
                        var innerValue = row[j] === null ? '' : row[j].toString();
                        if (row[j] instanceof Date) {
                            innerValue = row[j].toLocaleString();
                        };
                        var result = innerValue.replace(/"/g, '""');
                        if (result.search(/("|,|\n)/g) >= 0)
                            result = '"' + result + '"';
                        if (j > 0)
                            finalVal += ',';
                        finalVal += result;
                    }
                    return finalVal + '\n';
                };

                var csvFile = '';

                if (header.length > 0)
                    csvFile += processRow(header);

                for (var i = 0; i < rows.length; i++) {
                    csvFile += processRow(rows[i]);
                }

                var blob = new Blob([csvFile], { type: 'text/csv;charset=utf-8;' });
                $script('//cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2014-11-29/FileSaver.min.js', function() {
                    saveAs(blob, filename);
                });
            };
        })
        .service('tubularGridFilterService', [
            'tubulargGridFilterModel', '$compile', function tubularGridFilterService(FilterModel, $compile) {
                var me = this;

                me.applyFilterFuncs = function(scope, el, openCallback) {
                    scope.$component = scope.$parent.$component;
                    scope.filterTitle = "Filter";

                    scope.clearFilter = function() {
                        scope.filter.Operator = 'None';
                        scope.filter.Text = '';
                        scope.filter.Argument = [];

                        scope.$component.retrieveData();
                        scope.close();
                    };

                    scope.applyFilter = function() {
                        scope.$component.retrieveData();
                        scope.close();
                    };

                    scope.close = function() {
                        $(el).find('[data-toggle="popover"]').popover('hide');
                    };

                    $(el).find('[data-toggle="popover"]').popover({
                        html: true,
                        title: scope.filterTitle,
                        content: function() {
                            var selectEl = $(this).next().find('select').find('option').remove().end();
                            angular.forEach(scope.filterOperators, function(val, key) {
                                $(selectEl).append('<option value="' + key + '">' + val + '</option>');
                            });

                            return $compile($(this).next().html())(scope);
                        },
                    });

                    $(el).find('[data-toggle="popover"]').on('shown.bs.popover', openCallback);
                };

                me.createFilterModel = function(scope, lAttrs) {
                    scope.filter = new FilterModel(lAttrs);
                    scope.filter.Name = scope.$parent.column.Name;

                    var columns = scope.$component.columns.filter(function(el) {
                        return el.Name === scope.filter.Name;
                    });

                    if (columns.length === 0) return;

                    columns[0].Filter = scope.filter;
                    scope.dataType = columns[0].DataType;
                    scope.filterOperators = columns[0].FilterOperators[scope.dataType];

                    if (scope.dataType == 'datetime' || scope.dataType == 'date') {
                        scope.filter.Argument = [new Date()];
                    }

                    if (scope.dataType == 'numeric') {
                        scope.filter.Argument = [1];
                    }

                    scope.filterTitle = lAttrs.title || "Filter";
                };
            }
        ])
        .service('tubularEditorService', [
            function tubularEditorService() {
                var me = this;

                me.defaultScope = {
                    value: '=?',
                    state: '=?',
                    isEditing: '=?',
                    editorType: '@',
                    showLabel: '=?',
                    label: '=?',
                    required: '=?',
                    format: '=?',
                    min: '=?',
                    max: '=?',
                    name: '@',
                    defaultValue: '=?',
                    IsKey: '@'
                };

                me.setupScope = function(scope, defaultFormat) {
                    scope.isEditing = angular.isUndefined(scope.isEditing) ? true : scope.isEditing;
                    scope.showLabel = scope.showLabel || false;
                    scope.label = scope.label || (scope.name || '').replace(/([a-z])([A-Z])/g, '$1 $2');
                    scope.required = scope.required || false;
                    scope.format = scope.format || defaultFormat;
                    scope.$valid = true;
                    scope.$editorType = 'input';

                    if (angular.isUndefined(scope.defaultValue) == false && angular.isUndefined(scope.value)) {
                        scope.value = scope.defaultValue;
                    }

                    scope.$watch('value', function(newValue, oldValue) {
                        if (angular.isUndefined(oldValue) && angular.isUndefined(newValue)) return;

                        if (angular.isUndefined(scope.state)) {
                            scope.state = {
                                $valid: function() {
                                    return this.$errors == 0;
                                },
                                $errors: []
                            };
                        }

                        scope.$valid = true;
                        scope.state.$errors = [];

                        if (angular.isDefined(scope.$parent.Model)) {
                            if (angular.isDefined(scope.$parent.Model[scope.name]))
                                scope.$parent.Model[scope.name] = newValue;
                            else
                                scope.$parent.Model.$addField(scope.name, newValue);
                        }

                        if (angular.isUndefined(scope.value) && scope.required) {
                            scope.$valid = false;
                            scope.state.$errors = ["Field is required"];
                            return;
                        }

                        if (angular.isUndefined(scope.validate)) return;

                        scope.validate();
                    });

                    var parent = scope.$parent;

                    while (true) {
                        if (parent == null) break;
                        if (angular.isUndefined(parent.tubularDirective) == false &&
                            parent.tubularDirective == 'tubular-form') {
                            parent.addField(scope);
                            break;
                        }

                        parent = parent.$parent;
                    }
                };
            }
        ]);
})();<commit_msg>OData Sorting<commit_after>(function() {
    'use strict';

    // User Service based on https://bitbucket.org/david.antaramian/so-21662778-spa-authentication-example
    angular.module('tubular.services', ['ui.bootstrap'])
        .service('tubularHttp', [
            '$http', '$timeout', '$q', '$cacheFactory', '$cookieStore', function tubularHttp($http, $timeout, $q, $cacheFactory, $cookieStore) {
                function isAuthenticationExpired(expirationDate) {
                    var now = new Date();
                    expirationDate = new Date(expirationDate);
                    if (expirationDate - now > 0) {
                        return false;
                    } else {
                        return true;
                    }
                }

                function saveData() {
                    removeData();
                    $cookieStore.put('auth_data', me.userData);
                }

                function removeData() {
                    $cookieStore.remove('auth_data');
                }

                function retrieveSavedData() {
                    var savedData = $cookieStore.get('auth_data');
                    if (typeof savedData === 'undefined') {
                        throw new Exception('No authentication data exists');
                    } else if (isAuthenticationExpired(savedData.expirationDate)) {
                        throw new Exception('Authentication token has already expired');
                    } else {
                        me.userData = savedData;
                        setHttpAuthHeader();
                    }
                }

                function clearUserData() {
                    me.userData.isAuthenticated = false;
                    me.userData.username = '';
                    me.userData.bearerToken = '';
                    me.userData.expirationDate = null;
                }

                function setHttpAuthHeader() {
                    $http.defaults.headers.common.Authorization = 'Bearer ' + me.userData.bearerToken;
                }

                var me = this;
                me.userData = {
                    isAuthenticated: false,
                    username: '',
                    bearerToken: '',
                    expirationDate: null,
                };

                me.cache = $cacheFactory('tubularHttpCache');
                me.useCache = true;

                me.isAuthenticated = function() {
                    if (me.userData.isAuthenticated && !isAuthenticationExpired(me.userData.expirationDate)) {
                        return true;
                    } else {
                        try {
                            retrieveSavedData();
                        } catch (e) {
                            return false;
                        }
                        return true;
                    }
                };

                me.removeAuthentication = function() {
                    removeData();
                    clearUserData();
                    $http.defaults.headers.common.Authorization = null;
                };

                me.authenticate = function(username, password, successCallback, errorCallback, persistData) {
                    this.removeAuthentication();
                    var config = {
                        method: 'POST',
                        url: '/api/token',
                        headers: {
                            'Content-Type': 'application/x-www-form-urlencoded',
                        },
                        data: 'grant_type=password&username=' + username + '&password=' + password,
                    };

                    $http(config)
                        .success(function(data) {
                            me.userData.isAuthenticated = true;
                            me.userData.username = data.userName;
                            me.userData.bearerToken = data.access_token;
                            me.userData.expirationDate = new Date(data['.expires']);
                            setHttpAuthHeader();
                            if (persistData === true) {
                                saveData();
                            }
                            if (typeof successCallback === 'function') {
                                successCallback();
                            }
                        })
                        .error(function(data) {
                            if (typeof errorCallback === 'function') {
                                if (data.error_description) {
                                    errorCallback(data.error_description);
                                } else {
                                    errorCallback('Unable to contact server; please, try again later.');
                                }
                            }
                        });
                };

                me.saveDataAsync = function(model, request) {
                    var clone = angular.copy(model);
                    var originalClone = angular.copy(model.$original);

                    delete clone.$isEditing;
                    delete clone.$hasChanges;
                    delete clone.$selected;
                    delete clone.$original;
                    delete clone.$state;
                    delete clone.$valid;

                    request.data = {
                        Old: originalClone,
                        New: clone
                    };

                    var dataRequest = me.retrieveDataAsync(request);

                    dataRequest.promise.then(function(data) {
                        model.$hasChanges = false;
                        model.resetOriginal();
                        return data;
                    });

                    return dataRequest;
                };

                me.getExpirationDate = function() {
                    var date = new Date();
                    var minutes = 5;
                    return new Date(date.getTime() + minutes * 60000);
                }

                me.checksum = function(obj) {
                    var keys = Object.keys(obj).sort();
                    var output = [], prop;
                    for (var i = 0; i < keys.length; i++) {
                        prop = keys[i];
                        output.push(prop);
                        output.push(obj[prop]);
                    }
                    return JSON.stringify(output);
                };

                me.retrieveDataAsync = function(request) {
                    var checksum = me.checksum(request);

                    var canceller = $q.defer();

                    var cancel = function(reason) {
                        console.error(reason);
                        canceller.resolve(reason);
                    };

                    if ((request.requestMethod == 'GET' || request.requestMethod == 'POST') && me.useCache) {
                        var data = me.cache.get(checksum);

                        if (angular.isDefined(data) && data.Expiration.getTime() > new Date().getTime()) {
                            return {
                                promise: $q(function(resolve, reject) {
                                    resolve(data.Set);
                                }),
                                cancel: cancel
                            };
                        }
                    }

                    // TODO: Check Unauthorize error and send revalidation
                    var promise = $http({
                        url: request.serverUrl,
                        method: request.requestMethod,
                        data: request.data,
                        timeout: canceller.promise
                    }).then(function(response) {
                        $timeout.cancel(timeoutHanlder);

                        if (me.useCache) {
                            me.cache.put(checksum, { Expiration: me.getExpirationDate(), Set: response.data });
                        }

                        return response.data;
                    });

                    request.timeout = request.timeout || 15000;

                    var timeoutHanlder = $timeout(function() {
                        cancel('Timed out');
                    }, request.timeout);

                    return {
                        promise: promise,
                        cancel: cancel
                    };
                };

                me.get = function(url) {
                    return me.retrieveDataAsync({
                        serverUrl: url,
                        requestMethod: 'GET',
                    });
                };

                me.delete = function (url) {
                    return me.retrieveDataAsync({
                        serverUrl: url,
                        requestMethod: 'DELETE',
                    });
                };

                me.post = function (url, data) {
                    return me.retrieveDataAsync({
                        serverUrl: url,
                        requestMethod: 'POST',
                        data: data
                    });
                };

                me.put = function (url, data) {
                    return me.retrieveDataAsync({
                        serverUrl: url,
                        requestMethod: 'PUT',
                        data: data
                    });
                };
            }
        ])
        .service('tubularOData', ['tubularHttp', function tubularOData(tubularHttp) {
            var me = this;

            me.retrieveDataAsync = function (request) {
                var params = request.data;
                var url = request.serverUrl;
                url += url.indexOf('?') > 0 ? '&' : '?';
                url += '$format=json&$inlinecount=allpages';

                url += "&$select=" + params.Columns.map(function (el) { return el.Name; }).join(',');
                url += "&$skip=" + params.Skip;
                url += "&$top=" + params.Take;

                // TODO: Search: scope.search and filters in columns ($filter=Price gt 20)
                // TODO: Sorting $orderby using columns ($orderby=Rating,Category/Name desc)
                var order = params.Columns.filter(function (el) { return el.SortOrder > 0; }).sort(function (a, b) { return a.SortOrder - b.SortOrder; }).map(function (el) { return el.Name + " " + (el.SortDirection == "Descending" ? "desc" : ""); });

                if (order.length > 0)
                    url += "&$orderby=" + order.join(',');

                request.data = null;
                request.serverUrl = url;

                var response = tubularHttp.retrieveDataAsync(request);

                var promise = response.promise.then(function(data) {
                    var result = {
                        Payload: data.value,
                        CurrentPage: 1,
                        TotalPages: 1,
                        TotalRecordCount: 1,
                        FilteredRecordCount: 1
                    };

                    result.TotalRecordCount = data["odata.count"];
                    result.FilteredRecordCount = result.TotalRecordCount; // TODO
                    result.TotalPages = parseInt(result.TotalRecordCount / params.Take);
                    result.CurrentPage = parseInt(1 + ((params.Skip / result.FilteredRecordCount) * result.TotalPages));

                    return result;
                });

                return {
                    promise: promise,
                    cancel: response.cancel
                };
            };

            me.saveDataAsync = function (model, request) {
                tubularHttp.saveDataAsync(model, request);
            };

            me.get = function(url) {
                tubularHttp.get(url);
            };

            me.delete = function (url) {
                tubularHttp.delete(url);
            };

            me.post = function (url, data) {
                tubularHttp.post(url, data);
            };

            me.put = function(url, data) {
                tubularHttp.put(url, data);
            };
        }])
        .service('tubularGridPopupService', [
            '$modal', function tubularGridPopupService($modal) {
                var me = this;

                me.openDialog = function(template, model) {
                    var dialog = $modal.open({
                        templateUrl: template,
                        backdropClass: 'fullHeight',
                        controller: [
                            '$scope', function($scope) {
                                $scope.Model = model;

                                $scope.savePopup = function () {
                                    var saveResult = $scope.Model.save();

                                    if (saveResult)
                                        dialog.close();
                                };

                                $scope.closePopup = function() {
                                    $scope.Model.revertChanges();
                                    dialog.close();
                                };
                            }
                        ]
                    });
                };
            }
        ])
        .service('tubularGridExportService', function tubularGridExportService() {
            var me = this;

            me.getColumns = function(gridScope) {
                return gridScope.columns.map(function(c) { return c.Name.replace(/([a-z])([A-Z])/g, '$1 $2'); });
            };

            me.exportAllGridToCsv = function(filename, gridScope) {
                var columns = me.getColumns(gridScope);
                gridScope.getFullDataSource(function(data) {
                    me.exportToCsv(filename, columns, data);
                });

            };

            me.exportGridToCsv = function(filename, gridScope) {
                var columns = me.getColumns(gridScope);
                gridScope.currentRequest = {};
                me.exportToCsv(filename, columns, gridScope.dataSource.Payload);
                gridScope.currentRequest = null;
            };

            me.exportToCsv = function(filename, header, rows) {
                var processRow = function(row) {
                    var finalVal = '';
                    for (var j = 0; j < row.length; j++) {
                        var innerValue = row[j] === null ? '' : row[j].toString();
                        if (row[j] instanceof Date) {
                            innerValue = row[j].toLocaleString();
                        };
                        var result = innerValue.replace(/"/g, '""');
                        if (result.search(/("|,|\n)/g) >= 0)
                            result = '"' + result + '"';
                        if (j > 0)
                            finalVal += ',';
                        finalVal += result;
                    }
                    return finalVal + '\n';
                };

                var csvFile = '';

                if (header.length > 0)
                    csvFile += processRow(header);

                for (var i = 0; i < rows.length; i++) {
                    csvFile += processRow(rows[i]);
                }

                var blob = new Blob([csvFile], { type: 'text/csv;charset=utf-8;' });
                $script('//cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2014-11-29/FileSaver.min.js', function() {
                    saveAs(blob, filename);
                });
            };
        })
        .service('tubularGridFilterService', [
            'tubulargGridFilterModel', '$compile', function tubularGridFilterService(FilterModel, $compile) {
                var me = this;

                me.applyFilterFuncs = function(scope, el, openCallback) {
                    scope.$component = scope.$parent.$component;
                    scope.filterTitle = "Filter";

                    scope.clearFilter = function() {
                        scope.filter.Operator = 'None';
                        scope.filter.Text = '';
                        scope.filter.Argument = [];

                        scope.$component.retrieveData();
                        scope.close();
                    };

                    scope.applyFilter = function() {
                        scope.$component.retrieveData();
                        scope.close();
                    };

                    scope.close = function() {
                        $(el).find('[data-toggle="popover"]').popover('hide');
                    };

                    $(el).find('[data-toggle="popover"]').popover({
                        html: true,
                        title: scope.filterTitle,
                        content: function() {
                            var selectEl = $(this).next().find('select').find('option').remove().end();
                            angular.forEach(scope.filterOperators, function(val, key) {
                                $(selectEl).append('<option value="' + key + '">' + val + '</option>');
                            });

                            return $compile($(this).next().html())(scope);
                        },
                    });

                    $(el).find('[data-toggle="popover"]').on('shown.bs.popover', openCallback);
                };

                me.createFilterModel = function(scope, lAttrs) {
                    scope.filter = new FilterModel(lAttrs);
                    scope.filter.Name = scope.$parent.column.Name;

                    var columns = scope.$component.columns.filter(function(el) {
                        return el.Name === scope.filter.Name;
                    });

                    if (columns.length === 0) return;

                    columns[0].Filter = scope.filter;
                    scope.dataType = columns[0].DataType;
                    scope.filterOperators = columns[0].FilterOperators[scope.dataType];

                    if (scope.dataType == 'datetime' || scope.dataType == 'date') {
                        scope.filter.Argument = [new Date()];
                    }

                    if (scope.dataType == 'numeric') {
                        scope.filter.Argument = [1];
                    }

                    scope.filterTitle = lAttrs.title || "Filter";
                };
            }
        ])
        .service('tubularEditorService', [
            function tubularEditorService() {
                var me = this;

                me.defaultScope = {
                    value: '=?',
                    state: '=?',
                    isEditing: '=?',
                    editorType: '@',
                    showLabel: '=?',
                    label: '=?',
                    required: '=?',
                    format: '=?',
                    min: '=?',
                    max: '=?',
                    name: '@',
                    defaultValue: '=?',
                    IsKey: '@'
                };

                me.setupScope = function(scope, defaultFormat) {
                    scope.isEditing = angular.isUndefined(scope.isEditing) ? true : scope.isEditing;
                    scope.showLabel = scope.showLabel || false;
                    scope.label = scope.label || (scope.name || '').replace(/([a-z])([A-Z])/g, '$1 $2');
                    scope.required = scope.required || false;
                    scope.format = scope.format || defaultFormat;
                    scope.$valid = true;
                    scope.$editorType = 'input';

                    if (angular.isUndefined(scope.defaultValue) == false && angular.isUndefined(scope.value)) {
                        scope.value = scope.defaultValue;
                    }

                    scope.$watch('value', function(newValue, oldValue) {
                        if (angular.isUndefined(oldValue) && angular.isUndefined(newValue)) return;

                        if (angular.isUndefined(scope.state)) {
                            scope.state = {
                                $valid: function() {
                                    return this.$errors == 0;
                                },
                                $errors: []
                            };
                        }

                        scope.$valid = true;
                        scope.state.$errors = [];

                        if (angular.isDefined(scope.$parent.Model)) {
                            if (angular.isDefined(scope.$parent.Model[scope.name]))
                                scope.$parent.Model[scope.name] = newValue;
                            else
                                scope.$parent.Model.$addField(scope.name, newValue);
                        }

                        if (angular.isUndefined(scope.value) && scope.required) {
                            scope.$valid = false;
                            scope.state.$errors = ["Field is required"];
                            return;
                        }

                        if (angular.isUndefined(scope.validate)) return;

                        scope.validate();
                    });

                    var parent = scope.$parent;

                    while (true) {
                        if (parent == null) break;
                        if (angular.isUndefined(parent.tubularDirective) == false &&
                            parent.tubularDirective == 'tubular-form') {
                            parent.addField(scope);
                            break;
                        }

                        parent = parent.$parent;
                    }
                };
            }
        ]);
})();<|endoftext|>


<commit_before>function Lesson(attributes) {
    this.tags = [];
    for (let key in attributes) {
        if(key == 'tags') {
            const tags_array = attributes[key];
            for (let index in tags_array) {
                this.tags.push(new Tag(tags_array[index]));
            }
        } else if(key == 'notes') {
            this.notes = new Note(attributes['notes'][0]);
        } else {
            this[key] = attributes[key];
        }
    }

    this.typeIconName = (this.content_type == 'Lab' ?
        'fa-flask' :
        'fa-book'
    )
    this.statusIconName = (this.complete ?
        'fa-check-circle-o' :
        'fa-circle-o'
    )

    this.previous_lesson_link = (this.previous_lesson_id ?
      `/lessons/${this.previous_lesson_id}` :
      '/lessons'
    )

    this.next_lesson_link = (this.next_lesson_id ?
      `/lessons/${this.next_lesson_id}` :
      '/lessons'
    )

}

Lesson.prototype.hasNotes = function() {
    return this.notes.content != undefined;
}

Lesson.prototype.sortedTags = function() {
    return this.tags.sort((a, b) => {
        a.category > b.category ? 1 : 0;
    })
}

Lesson.prototype.displayLesson = function() {
    $('#lesson-content').html(Lesson.template(this));
    this.addListeners();
}

Lesson.prototype.addListeners = function() {
    this.pageButtonListener();
    this.notesButtonListener();
}

Lesson.prototype.pageButtonListener = function() {
    $('.page-button').parent().on('click', function(e) {
        e.preventDefault();

        const url = $(this).attr('href');
        if (url == '/lessons') {
            return window.location.href = url;
        }

        Lesson.getLesson(url);
    })
}

Lesson.prototype.notesButtonListener = function() {
    const lesson = this;
    $('.notes-button').parent().on('click', function(e) {
        e.preventDefault();
        if ($('.notes-button').text() == 'Edit') {
            lesson.showForm();
        } else {
            lesson.submitForm();
        }
    })
}

Lesson.prototype.showForm = function() {
    $('#notes-show').css('display','none');

    const $form = $('#notes-edit form')
    if (this.hasNotes()) {
        $form.attr('method', 'patch')
        $form.attr('action', `/lessons/${this.id}/notes/${this.notes.id}`)
        $('#note_content').val(this.notes.content)
    }
    $('.notes-button').text('Save');

    $('#notes-edit').css('display','block');
}

Lesson.prototype.submitForm = function() {
    const lesson = this;
    const $form = $('#notes-edit form');

    $.ajax({
        type: $form.attr('method'),
        url: $form.attr('action'),
        data: $form.serialize(),
        dataType: 'json'
    })
    .success(function(data) {
        const note = new Note(data)
        if (note.content != "") {
            lesson.notes = note;
        }
        lesson.displayLesson();
    })
    .fail(function(data) {
        errorMessage(`Oops! Failed to save: '${data.responseJSON.message}'.`);
    });
}

Lesson.initializeHandlebars = function initializeHandlebars() {
    Lesson.templateSrc = $('#lesson-template').html();
    Lesson.template = Handlebars.compile(Lesson.templateSrc);

    Lesson.tagsPartialSrc = $('#lesson-tags-partial').html();
    Handlebars.registerPartial('tagsPartial', Lesson.tagsPartialSrc);

    Lesson.notesPartialSrc = $('#lesson-notes-partial').html();
    Handlebars.registerPartial('notesPartial', Lesson.notesPartialSrc);
}

Lesson.getLesson = function(url) {
    $.ajax({
        url: url,
        dataType: 'json',
        method: 'GET'
    })
    .success((data) => {
        const lesson = new Lesson(data);
        lesson.displayLesson();
        window.history.pushState(null, null, `/lessons/${lesson.id}.html`);
    })
    .fail((response) => {
        errorMessage(`Oops! Failed to load '${url}'.`);
    });
}

$(function() {
    if ($('body').hasClass("lessons show")) {
        Lesson.initializeHandlebars();
        Lesson.getLesson($('#lesson-content').data().lessonUrl);
    }
})<commit_msg>refactor code from showForm into setupFormForEdit<commit_after>function Lesson(attributes) {
    this.tags = [];
    for (let key in attributes) {
        if(key == 'tags') {
            const tags_array = attributes[key];
            for (let index in tags_array) {
                this.tags.push(new Tag(tags_array[index]));
            }
        } else if(key == 'notes') {
            this.notes = new Note(attributes['notes'][0]);
        } else {
            this[key] = attributes[key];
        }
    }

    this.typeIconName = (this.content_type == 'Lab' ?
        'fa-flask' :
        'fa-book'
    )
    this.statusIconName = (this.complete ?
        'fa-check-circle-o' :
        'fa-circle-o'
    )

    this.previous_lesson_link = (this.previous_lesson_id ?
      `/lessons/${this.previous_lesson_id}` :
      '/lessons'
    )

    this.next_lesson_link = (this.next_lesson_id ?
      `/lessons/${this.next_lesson_id}` :
      '/lessons'
    )

}

Lesson.prototype.hasNotes = function() {
    return this.notes.content != undefined;
}

Lesson.prototype.sortedTags = function() {
    return this.tags.sort((a, b) => {
        a.category > b.category ? 1 : 0;
    })
}

Lesson.prototype.displayLesson = function() {
    $('#lesson-content').html(Lesson.template(this));
    this.addListeners();
}

Lesson.prototype.addListeners = function() {
    this.pageButtonListener();
    this.notesButtonListener();
}

Lesson.prototype.pageButtonListener = function() {
    $('.page-button').parent().on('click', function(e) {
        e.preventDefault();

        const url = $(this).attr('href');
        if (url == '/lessons') {
            return window.location.href = url;
        }

        Lesson.getLesson(url);
    })
}

Lesson.prototype.notesButtonListener = function() {
    const lesson = this;
    $('.notes-button').parent().on('click', function(e) {
        e.preventDefault();
        if ($('.notes-button').text() == 'Edit') {
            lesson.showForm();
        } else {
            lesson.submitForm();
        }
    })
}

Lesson.prototype.setupFormForEdit = function() {
    const $form = $('#notes-edit form')
    $form.attr('method', 'patch')
    $form.attr('action', `/lessons/${this.id}/notes/${this.notes.id}`)
    $('#note_content').val(this.notes.content)
}

Lesson.prototype.showForm = function() {
    $('#notes-show').css('display','none');

    if (this.hasNotes()) { this.setupFormForEdit() }
    $('.notes-button').text('Save');

    $('#notes-edit').css('display','block');
}

Lesson.prototype.submitForm = function() {
    const lesson = this;
    const $form = $('#notes-edit form');

    $.ajax({
        type: $form.attr('method'),
        url: $form.attr('action'),
        data: $form.serialize(),
        dataType: 'json'
    })
    .success(function(data) {
        const note = new Note(data)
        if (note.content != "") {
            lesson.notes = note;
        }
        lesson.displayLesson();
    })
    .fail(function(data) {
        errorMessage(`Oops! Failed to save: '${data.responseJSON.message}'.`);
    });
}

Lesson.initializeHandlebars = function initializeHandlebars() {
    Lesson.templateSrc = $('#lesson-template').html();
    Lesson.template = Handlebars.compile(Lesson.templateSrc);

    Lesson.tagsPartialSrc = $('#lesson-tags-partial').html();
    Handlebars.registerPartial('tagsPartial', Lesson.tagsPartialSrc);

    Lesson.notesPartialSrc = $('#lesson-notes-partial').html();
    Handlebars.registerPartial('notesPartial', Lesson.notesPartialSrc);
}

Lesson.getLesson = function(url) {
    $.ajax({
        url: url,
        dataType: 'json',
        method: 'GET'
    })
    .success((data) => {
        const lesson = new Lesson(data);
        lesson.displayLesson();
        window.history.pushState(null, null, `/lessons/${lesson.id}.html`);
    })
    .fail((response) => {
        errorMessage(`Oops! Failed to load '${url}'.`);
    });
}

$(function() {
    if ($('body').hasClass("lessons show")) {
        Lesson.initializeHandlebars();
        Lesson.getLesson($('#lesson-content').data().lessonUrl);
    }
})<|endoftext|>


<commit_before>// init Isotope
var $grid = $('.photos-grid').isotope({
  percentPosition: true,
  masonry: {
    columnWidth: '.photo-grid-sizer'
  }
  itemSelector: '.photo-grid-item'
});
<commit_msg>Isotope not needed for photo thumbnails<commit_after><|endoftext|>


<commit_before>$(document).ready(function() {
	// this is for custom formating of results
   $.extend( $.ui.autocomplete.prototype, {

    _renderItem: function( ul, item) {
      var list = "

";
      return $( list )
        .data( "item.autocomplete", item )
        .append( $( "" ).html( item.html || item.label ) )
        .appendTo( ul );
    }
  });

  var searchUrl = function(pathSuffix) {
    var url = $("form[role=search]").attr("action") ? $("form[role=search]").attr("action") : '/search';
    if (pathSuffix !== undefined) {
      url = url.replace(/search$/, pathSuffix);
    }
    return url;
  };

  var preloaded_search_data = false;
  /* Preload some common searches into the autocomplete box */
  $.getJSON(searchUrl("preload-autocomplete"), function(data) {
    if (data && data[0] && data[0].link && data[0].title && data[0].format) {
      preloaded_search_data = data.map( function(e) {
        return { 'label': e.title, 'url': e.link, 'html_class': e.format };
      });
    }
  });

  var filter_terms = function(search_term,data) {
    var highlight_term = function(query, term) {
      if (query == undefined) { return false; }

      var term = term.split(' ')[0];
      var regex = new RegExp("\\b("+term+")","ig");
      var count = 0;
      if (query.match(regex)) {
        return query;
      } else {
        return false;
      }
    };

    return $.map(data, function(item) {

      var term = highlight_term(item.label, search_term);
      if (term && item.html_class && item.url) {
        return {
          html_class: item.html_class,
          label:  term,
          url:   item.url
        };
      }
    });
  };

	/* Smoke and mirrors search hint */

  function hintPlaceholder(element) {
    if($(".hint-suggest").length == 0) {
      if (element.attr('id') == 'main_autocomplete') {
  	    $("#search_hint").after("Type for suggestions");
        $("#search_hint").addClass("visuallyhidden");
      }
      else {
        var attachPoint = element.parent("fieldset");
        attachPoint.append("Type for suggestions");
      }
    }
    return $('.hint-suggest');
  };

  $("#main_autocomplete, #site-search-text").live("focus", function(){
    hintPlaceholder($(this)).text('Type for suggestions');
  });

	$("#site-search-text, #main_autocomplete").live("blur", function(){
	  $(".hint-suggest").remove();
	  $("#search_hint").removeClass("visuallyhidden");
	});

  $("#site-search-text, #main_autocomplete").keydown( function() {
    if ($(this).val().length > 3) {
      $(this).autocomplete( "option", "delay", 400);
    } else {
      $(this).autocomplete( "option", "delay", 100);
    }

    if ($(".ui-autocomplete-input").attr("value").length == 0) {
      $(".hint-suggest").remove();
      $("#search_hint").removeClass("visuallyhidden");
    }

  });

  var results_found = -1;

  $("#main_autocomplete, #site-search-text").autocomplete({
    delay: 100,
    width: 300,
    source: function(req, add){
      var hint = hintPlaceholder(this.element);
      var preloaded_results = false;
      if (preloaded_search_data) {
        var preloaded_results = filter_terms(req.term, preloaded_search_data)
      }

      if (req.term.length > 3 || preloaded_results == false || preloaded_results.length == 0) {
        results_found = 0;
        $.ajax({
          url: searchUrl("autocomplete") + "?q=" + req.term,
          dataType: "json",
          cache: true,
          beforeSend: function(xhr) {
            hint.text("Loading...");
            hint.addClass("search-loading");
          },
          success: function(data) {
            var results = $.map(data, function(e) {
              return { 'label': e.title, 'url': e.link, 'class': e.format };
            });
            results_found = results.length;
            add(results)
          },
          complete: function(jqXHR, textstatus){
            hint.removeClass("search-loading");
            if (textstatus != 'success' || results_found == 0) {
              $(".hint-suggest").remove();
              $("#search_hint").removeClass("visuallyhidden");
            }
          }
        });
      } else {
        if (preloaded_results.length > 5) {
          var preloaded_results = preloaded_results.splice(0, 5);
        }
        add( preloaded_results );
      }
    },
    select: function(event, ui) {
      location.href = ui.item.url;
    },
    open: function(event, ui){
      if($("#site-search-text").length != 0){
        // all this just to move the ul to the left by an offset
        var offset = $("#site-search-text").offset(),
        leftoffset = offset.left,
        width = $("#site-search-text").width();

        var newLeft = (leftoffset - width);
        $(".ui-autocomplete").css("left", newLeft+"px").css("width", ((width*2)+4)+"px");
      }
      // quickly add the search value to end of list
      var searchVal = $(".ui-autocomplete-input").attr("value");
      $(".ui-autocomplete").append("
Search for "+searchVal+"
");
      $("#search_hint").addClass('visuallyhidden');
    },
    close: function(event, ui){
      if ($(".ui-autocomplete-input").attr("value").length == 0) {
        $(".hint-suggest").remove();
        $("#search_hint").removeClass("visuallyhidden");
      }
    }
  });

});
<commit_msg>Use 0 delay when loading from local data source.<commit_after>$(document).ready(function() {
	// this is for custom formating of results
   $.extend( $.ui.autocomplete.prototype, {

    _renderItem: function( ul, item) {
      var list = "

";
      return $( list )
        .data( "item.autocomplete", item )
        .append( $( "" ).html( item.html || item.label ) )
        .appendTo( ul );
    }
  });

  var searchUrl = function(pathSuffix) {
    var url = $("form[role=search]").attr("action") ? $("form[role=search]").attr("action") : '/search';
    if (pathSuffix !== undefined) {
      url = url.replace(/search$/, pathSuffix);
    }
    return url;
  };

  var preloaded_search_data = false;
  /* Preload some common searches into the autocomplete box */
  $.getJSON(searchUrl("preload-autocomplete"), function(data) {
    if (data && data[0] && data[0].link && data[0].title && data[0].format) {
      preloaded_search_data = data.map( function(e) {
        return { 'label': e.title, 'url': e.link, 'html_class': e.format };
      });
    }
  });

  var filter_terms = function(search_term,data) {
    var highlight_term = function(query, term) {
      if (query == undefined) { return false; }

      var term = term.split(' ')[0];
      var regex = new RegExp("\\b("+term+")","ig");
      var count = 0;
      if (query.match(regex)) {
        return query;
      } else {
        return false;
      }
    };

    return $.map(data, function(item) {

      var term = highlight_term(item.label, search_term);
      if (term && item.html_class && item.url) {
        return {
          html_class: item.html_class,
          label:  term,
          url:   item.url
        };
      }
    });
  };

	/* Smoke and mirrors search hint */

  function hintPlaceholder(element) {
    if($(".hint-suggest").length == 0) {
      if (element.attr('id') == 'main_autocomplete') {
  	    $("#search_hint").after("Type for suggestions");
        $("#search_hint").addClass("visuallyhidden");
      }
      else {
        var attachPoint = element.parent("fieldset");
        attachPoint.append("Type for suggestions");
      }
    }
    return $('.hint-suggest');
  };

  function useLocalDataSource(element) {
    return $(element).val().length <= 3;
  }

  $("#main_autocomplete, #site-search-text").live("focus", function(){
    hintPlaceholder($(this)).text('Type for suggestions');
  });

	$("#site-search-text, #main_autocomplete").live("blur", function(){
	  $(".hint-suggest").remove();
	  $("#search_hint").removeClass("visuallyhidden");
	});

  $("#site-search-text, #main_autocomplete").keydown( function() {
    if (useLocalDataSource(this)) {
      $(this).autocomplete( "option", "delay", 0);
    } else {
      $(this).autocomplete( "option", "delay", 400);
    }

    if ($(".ui-autocomplete-input").attr("value").length == 0) {
      $(".hint-suggest").remove();
      $("#search_hint").removeClass("visuallyhidden");
    }
  });

  var results_found = -1;

  $("#main_autocomplete, #site-search-text").autocomplete({
    delay: 100,
    width: 300,
    source: function(req, add){
      var hint = hintPlaceholder(this.element);
      var preloaded_results = false;
      if (preloaded_search_data) {
        var preloaded_results = filter_terms(req.term, preloaded_search_data)
      }

      if (req.term.length > 3 || preloaded_results == false || preloaded_results.length == 0) {
        results_found = 0;
        $.ajax({
          url: searchUrl("autocomplete") + "?q=" + req.term,
          dataType: "json",
          cache: true,
          beforeSend: function(xhr) {
            hint.text("Loading...");
            hint.addClass("search-loading");
          },
          success: function(data) {
            var results = $.map(data, function(e) {
              return { 'label': e.title, 'url': e.link, 'class': e.format };
            });
            results_found = results.length;
            add(results)
          },
          complete: function(jqXHR, textstatus){
            hint.removeClass("search-loading");
            if (textstatus != 'success' || results_found == 0) {
              $(".hint-suggest").remove();
              $("#search_hint").removeClass("visuallyhidden");
            }
          }
        });
      } else {
        if (preloaded_results.length > 5) {
          var preloaded_results = preloaded_results.splice(0, 5);
        }
        add( preloaded_results );
      }
    },
    select: function(event, ui) {
      location.href = ui.item.url;
    },
    open: function(event, ui){
      if($("#site-search-text").length != 0){
        // all this just to move the ul to the left by an offset
        var offset = $("#site-search-text").offset(),
        leftoffset = offset.left,
        width = $("#site-search-text").width();

        var newLeft = (leftoffset - width);
        $(".ui-autocomplete").css("left", newLeft+"px").css("width", ((width*2)+4)+"px");
      }
      // quickly add the search value to end of list
      var searchVal = $(".ui-autocomplete-input").attr("value");
      $(".ui-autocomplete").append("
Search for "+searchVal+"
");
      $("#search_hint").addClass('visuallyhidden');
    },
    close: function(event, ui){
      if ($(".ui-autocomplete-input").attr("value").length == 0) {
        $(".hint-suggest").remove();
        $("#search_hint").removeClass("visuallyhidden");
      }
    }
  });

});
<|endoftext|>


<commit_before>import React, {Component, PropTypes} from 'react'
import { Panel, Glyphicon } from 'react-bootstrap'

import { getProjectCost, formatCost } from '../util'

// TODO: fix this
require('../../node_modules/rc-switch/assets/index.css')
var Switch = require('rc-switch');

export default class ProjectListing extends Component {

  constructor (props) {
    super(props)
  }

  render () {

    const mainStyle = this.props.inverse
      ? {
          color: '#444',
          fontFamily: "Lato"
        }
      : {}

    const titleStyle = {
      fontSize: '20px',
      fontWeight: 'bold',
      marginBottom: '3px'
    }

    const descStyle = {
      fontSize: '12px',
      hyphens: 'auto'
    }

    const costStyle = {
      float:'right',
      background: this.props.inverse ? '#ddd' : '#444',
      padding: '3px 6px',
      fontSize: '15px',
      fontWeight: 'bold',
      marginLeft: '4px'
    }

    const project = this.props.project
    return (

          <Switch onChange={() => { this.props.projectToggled() }}
            checked={project.selected}
            checkedChildren={<Glyphicon glyph='ok' />}
            unCheckedChildren={<Glyphicon glyph='remove' />}
            style={{ marginRight: '10px' }}
          />

{formatCost(getProjectCost(project))}

          {project.name}

          {project.description}

    )
  }
}
<commit_msg>Project list styling<commit_after>import React, {Component, PropTypes} from 'react'
import { Panel, Glyphicon } from 'react-bootstrap'

import { getProjectCost, formatCost } from '../util'

// TODO: fix this
require('../../node_modules/rc-switch/assets/index.css')
var Switch = require('rc-switch');

export default class ProjectListing extends Component {

  constructor (props) {
    super(props)
  }

  render () {

    const mainStyle = this.props.inverse
      ? {
          color: '#444',
          fontFamily: "Lato",
        }
      : {
          marginBottom: '18px'
        }

    const headerStyle = {
      marginBottom: '6px'
    }

    const switchStyle = {
      float: 'left',
      marginTop: '3px'
    }

    const titleStyle = {
      fontSize: '20px',
      lineHeight: '22px',
      fontWeight: 'bold',
      marginLeft: '52px',
      marginTop: '-4px'
    }

    const descStyle = {
      fontSize: '12px',
      hyphens: 'auto'
    }

    const costStyle = {
      float:'right',
      background: this.props.inverse ? '#ddd' : '#444',
      padding: '1px 5px',
      fontSize: '15px',
      fontWeight: 'bold',
      marginLeft: '4px'
    }

    const project = this.props.project
    return (

          <Switch onChange={() => { this.props.projectToggled() }}
            checked={project.selected}
            checkedChildren={<Glyphicon glyph='ok' />}
            unCheckedChildren={<Glyphicon glyph='remove' />}
            style={switchStyle}
          />

{formatCost(getProjectCost(project))}

            {project.name}

          {project.description}

    )
  }
}
<|endoftext|>


<commit_before>import Ember from 'ember';

export default Ember.Component.extend({
  userSettings: Ember.inject.service('user-settings'),
  sortAscending: false,

  init () {
    this._super();
    this.set('currency', this.get('userSettings').currency());
  },

  actions: {
    sortByProperty (property) {
      this.sortValues(property);
    },

    tableViewSettings (settings) {
      if (this.get('tableViewSettings') === settings) { return; }

      $('.table-view-settings button').removeClass('mdl-button--raised');
      $(`#btn-${settings}`).addClass('mdl-button--raised');
      this.set('tableViewSettings', settings);
    }
  },

  sortValues (property) {
    if (this.get('sortAscending')) {
      const dataSorted = this.get('data').sortBy(property);
      this.set('data', dataSorted);
    } else {
      const dataSorted = this.get('data').sortBy(property).reverse();
      this.set('data', dataSorted);
    }

    this.set('sortAscending', !this.get('sortAscending'));
  }
});
<commit_msg>feat: Filter table content on search query or table setting buttons<commit_after>import Ember from 'ember';
import $ from 'jquery';

export default Ember.Component.extend({
  userSettings: Ember.inject.service('user-settings'),
  sortAscending: false,
  searchQuery: '',
  tableViewSettings: 'all',

  init () {
    this._super();
    this.set('currency', this.get('userSettings').currency());
  },

  actions: {
    sortByProperty (property) {
      this.sortValues(property);
    },

    tableViewSettings (settings) {
      if (this.get('tableViewSettings') === settings) { return; }

      $('.table-view-settings button').removeClass('mdl-button--raised');
      $(`#btn-${settings}`).addClass('mdl-button--raised');
      this.set('tableViewSettings', settings);
    }
  },

  sortValues (property) {
    if (this.get('sortAscending')) {
      const dataSorted = this.get('data').sortBy(property);
      this.set('data', dataSorted);
    } else {
      const dataSorted = this.get('data').sortBy(property).reverse();
      this.set('data', dataSorted);
    }

    this.set('sortAscending', !this.get('sortAscending'));
  },

  tableContent: function () {
    let filter = this.get('searchQuery');
    let data = this.setContentView(this.get('data'));

    if (this.get('searchQuery').length) {
      // Display data based on searchQuery
      let filteredContent = data.filter(function (item, index, enumerable) {
        return item.get('name').toLowerCase().match(filter.toLowerCase()) || item.get('category').toLowerCase().match(filter.toLowerCase());
      });
      return filteredContent;
    }

    // Display latest on top
    // Default values if search field empty
    return data;
  }.property('searchQuery', 'data.@each', 'tableViewSettings'),
  }
});
<|endoftext|>


<commit_before>import FileUploadComponent from 'appkit/components/file-upload';

export default FileUploadComponent.extend({
  selectAccept  : 'image/*',
  selectMultiple: true,
  defaultClasses: 'icon-picture',
  successMsg    : ' Image upload complete.',

  // keep control value synced with items
  updateValue: function () {
    this.set('control.value', this.get('items').filterBy('image.url').getEach('image'));
  }.observes('items.@each.image'),

  willInsertElement: function () {

    // create initial set of items from control value
    this.set('items', Ember.A(this.get('control.value')).map(function (image) {
      return Ember.Object.create({
        image: image
      });
    }));

  },

  didInsertElement: function () {
    this._super.apply(this, arguments);
    Ember.$(window).on('click', this.closeEdit.bind(this));

    this.$().on('click', '.wy-form-gallery-edit', function (event) {
      event.stopPropagation();
    });
  },

  willDestroyElement: function () {
    this._super.apply(this, arguments);
    Ember.$(window).off('click', this.closeEdit.bind(this));
  },

  // Override default behavior.
  // Keep track of progress for each image.
  selectedFile: function (file) {

    var item = Ember.Object.create();

    var uploading = this.uploader.upload(file);

    uploading.progress(function (event) {
      item.set('progress', Math.ceil((event.loaded * 100) / event.total));
    });

    uploading.done(function (response) {
      item.set('progress', null);
      item.set('image', { url: response.url });
      this.sendAction('notify', 'success', this.get('successMsg'));
    }.bind(this));

    this.get('items').pushObject(item);

  },

  closeEdit: function () {
    this.get('items').setEach('editing', null);
  },

  actions: {
    removeImage: function (item) {
      this.get('control.value').removeObject(item.image);
      this.get('items').removeObject(item);
    },
    editImage: function (item) {
      var wasEditing = item.get('editing');
      this.closeEdit();
      if (!wasEditing) {
        item.set('editing', true);
      }
    },
    closeEdit: function () {
      this.closeEdit();
    }
  }
});
<commit_msg>dot dot dot for urls<commit_after>import FileUploadComponent from 'appkit/components/file-upload';

export default FileUploadComponent.extend({
  selectAccept  : 'image/*',
  selectMultiple: true,
  defaultClasses: 'icon-picture',
  successMsg    : ' Image upload complete.',

  // keep control value synced with items
  updateValue: function () {
    this.set('control.value', this.get('items').filterBy('image.url').getEach('image'));
  }.observes('items.@each.image'),

  willInsertElement: function () {

    // create initial set of items from control value
    this.set('items', Ember.A(this.get('control.value')).map(function (image) {
      return Ember.Object.create({
        image: image
      });
    }));

  },

  didInsertElement: function () {
    this._super.apply(this, arguments);
    Ember.$(window).on('click', this.closeEdit.bind(this));

    this.$().on('click', '.wy-form-gallery-edit', function (event) {
      event.stopPropagation();
    });
  },

  willDestroyElement: function () {
    this._super.apply(this, arguments);
    Ember.$(window).off('click', this.closeEdit.bind(this));
  },

  // Override default behavior.
  // Keep track of progress for each image.
  selectedFile: function (file) {

    var item = Ember.Object.create();

    var uploading = this.uploader.upload(file);

    // have something to start
    item.set('progress', '...');

    uploading.progress(function (event) {
      item.set('progress', Math.ceil((event.loaded * 100) / event.total));
    });

    uploading.done(function (response) {
      item.set('progress', null);
      item.set('image', { url: response.url });
      this.sendAction('notify', 'success', this.get('successMsg'));
    }.bind(this));

    this.get('items').pushObject(item);

  },

  closeEdit: function () {
    this.get('items').setEach('editing', null);
  },

  actions: {
    removeImage: function (item) {
      this.get('control.value').removeObject(item.image);
      this.get('items').removeObject(item);
    },
    editImage: function (item) {
      var wasEditing = item.get('editing');
      this.closeEdit();
      if (!wasEditing) {
        item.set('editing', true);
      }
    },
    closeEdit: function () {
      this.closeEdit();
    }
  }
});
<|endoftext|>


<commit_before>/* Site survey creation screen with validation */

// Tabbed Bar Labels
var pickBiomeLabels = [];
var pickProtocolLabels = [];

// Populate the biome TabbedBar with database-derived labels
try {
	var db = Ti.Database.open('ltemaDB');
	var biomeResultRows = db.execute('SELECT biome_name FROM biome ');
	while (biomeResultRows.isValidRow()) {
		var biomeName = biomeResultRows.fieldByName('biome_name');
		pickBiomeLabels.push({title:biomeName, enabled:true});
		biomeResultRows.next();
	}
} catch(e) {
	var errorMessage = e.message;
	Ti.App.fireEvent("app:dataBaseError", {error: errorMessage});
} finally {
	biomeResultRows.close();
	db.close();
	$.pickBiome.labels = pickBiomeLabels;
}

/* Nav Bar Label */

// Build title label

var labelText = 'New Site Survey';

var titleLabel = Titanium.UI.createLabel({
	top:10,
	text: labelText,
	textAlign:'center',
	font:{fontSize:20,fontWeight:'bold'},
});

// Associate label to title
$.addSiteSurveyWin.setTitleControl(titleLabel);

// Instruciton text
var instructions = "Start typing in the search bar to find a park.\n\n" +
					"Picking a Biome will show its Protocols to choose from.\n\n" +
					"Pick a Protocol and click the Done button to create the new site survey.\n\n" +
					"\n" +
					"LTEMA currently supports the Grassland and Alpine protocols.";
$.info.text = instructions;

/* Event Listeners */

// Regenerate protocol TabbedBar based on biome selected
$.pickBiome.addEventListener('click', function(e) {
	//remove old list
	$.pickProtocol.index = -1;
	$.pickBiomeError.visible = false;
	$.pickProtocolError.visible = false;
	while (pickProtocolLabels.length > 0) {
		pickProtocolLabels.pop();
	}
	//populate new list based on new biome selected
	try {
		var db = Ti.Database.open('ltemaDB');
		var protocolResultRows = db.execute('SELECT protocol_name \
											FROM protocol p, biome b \
											WHERE p.biome_id = b.biome_id \
											AND  b.biome_name =?', pickBiomeLabels[e.index].title);
		while (protocolResultRows.isValidRow()) {
			var protocolName = protocolResultRows.fieldByName('protocol_name');
			pickProtocolLabels.push({title:protocolName, enabled:true});
			protocolResultRows.next();
		}
	} catch (e) {
		var errorMessage = e.message;
		Ti.App.fireEvent("app:dataBaseError", {error: errorMessage});
	} finally {
		protocolResultRows.close();
		db.close();
	}

	//refresh TabbedBar
	$.pickProtocol.labels = pickProtocolLabels;

	//TODO: obtain user feedback, disabled for testing
	//auto-select protocol if there's only one
	//if ($.pickProtocol.labels.length == 1) {
	//	$.pickProtocol.index = 0;
	//}
});

// Check for unsupported protocols
$.pickProtocol.addEventListener('click', function(e) {
	if ((pickProtocolLabels[e.index].title !== "Alpine") && (pickProtocolLabels[e.index].title !== "Grassland")) {
		$.pickProtocolError.text = "Unsupported protocol by LTEMA at this time";
		$.pickProtocolError.visible = true;
	} else {
		$.pickProtocolError.visible = false;
	}
});

// Closes the popup result window if user navigates away from this screen
// (improves performance related to issue #14)
$.parkSrch.addEventListener('blur', function(e) {
	win.close();
});

// related to issue #28
$.addSiteSurveyWin.addEventListener('close', function(e) {
	Ti.App.fireEvent("app:refreshSiteSurveys");
});

/* Functions */

//Test for form completeness before adding to database
function doneBtn(e){
	//disable button for 1 second to prevent double entry
	e.source.enabled = false;
	setTimeout(function(){ e.source.enabled = true; },1000);

	var errorFlag = false;
	if (($.parkSrch.value == null) || ($.parkSrch.value == "")) {
		$.parkSrchError.text = "Please select a park";
		$.parkSrchError.visible = true;
		errorFlag = true;
	}
	if ($.pickBiome.index == null) {
		$.pickBiomeError.text = "Please select a biome";
		$.pickBiomeError.visible = true;
		errorFlag - true;
	}
	if (($.pickProtocol.index == null) || ($.pickProtocol.index == -1)) {
		$.pickProtocolError.text = "Please select a protocol";
		$.pickProtocolError.visible = true;
		errorFlag = true;
	} else if (($.pickProtocol.labels[$.pickProtocol.index].title !== "Alpine") && ($.pickProtocol.labels[$.pickProtocol.index].title !== "Grassland")) {
		$.pickProtocolError.text = "Unsupported protocol by LTEMA at this time";
		$.pickProtocolError.visible = true;
		errorFlag = true;
	}

	if (errorFlag === true) {
		return;
	} else if (errorFlag === false) {
		$.parkSrchError.visible = false;
		$.pickBiomeError.visible = false;
		$.pickProtocolError.visible = false;
		try {
			var db = Ti.Database.open('ltemaDB');
			var currentYear = new Date().getFullYear().toString();
			var protocolResult = db.execute('SELECT protocol_id FROM protocol WHERE protocol_name =?', $.pickProtocol.labels[$.pickProtocol.index].title);
			var protocolID = protocolResult.fieldByName('protocol_id');
			var parkResult = db.execute('SELECT park_id FROM park WHERE park_name =?', $.parkSrch.value);
			var parkID = parkResult.fieldByName('park_id');

			// Check if this site has been previously surveyed
			var previousSurveys = db.execute('SELECT site_id FROM site_survey \
											WHERE protocol_id = ? \
											AND park_id = ?', protocolID, parkID);

			// Get the most recently entered survey
			var id;
			while (previousSurveys.isValidRow()) {
				id = previousSurveys.fieldByName('site_id');
				previousSurveys.next();
			}

			// Insert the new survey
			db.execute( 'INSERT INTO site_survey (year, protocol_id, park_id) VALUES (?,?,?)', currentYear, protocolID, parkID);

			//get the id of the last row inserted into the database - *not sure if this is acceptable sql code to use?
			var results = db.execute('SELECT last_insert_rowid() as siteID');
			var siteID = results.fieldByName('siteID');

			// Get the transects associated with the survey
			if (id != null) {
				var transects = db.execute('SELECT * FROM transect WHERE site_id = ?', id);

				// Copy and associate any exiting transects
				while (transects.isValidRow()) {
					var transectName = transects.fieldByName('transect_name');
					var surveyor = transects.fieldByName('surveyor');
					var otherSurveyors = transects.fieldByName('other_surveyors');
					var plotDistance = transects.fieldByName('plot_distance');
					var stakeOrientation = transects.fieldByName('stake_orientation');
					var utmZone = transects.fieldByName('utm_zone');
					var utmEasting = transects.fieldByName('utm_easting');
					var utmNorthing = transects.fieldByName('utm_northing');
					var tComments = transects.fieldByName('comments');
					var transectID = transects.fieldByName('transect_id');

					db.execute('INSERT INTO transect (transect_name, surveyor, other_surveyors, plot_distance, stake_orientation, \
						utm_zone, utm_easting, utm_northing, comments, site_id) \
						VALUES (?,?,?,?,?,?,?,?,?,?)', transectName, surveyor, otherSurveyors, plotDistance, stakeOrientation, utmZone,
						utmEasting, utmNorthing, tComments, siteID);

					// Get the transect_id for the last row inserted
					results = db.execute('SELECT last_insert_rowid() as transectID');
					var newTransectID = results.fieldByName('transectID');

					// Get any plots associated with the transect
					var plots = db.execute('SELECT * FROM plot WHERE transect_id = ?', transectID);

					// Copy and associate any existing plots
					while (plots.isValidRow()) {
						var plotName = plots.fieldByName('plot_name');
						var plotUtmZone = plots.fieldByName('utm_zone');
						var plotUtmEasting = plots.fieldByName('utm_easting');
						var plotUtmNorthing = plots.fieldByName('utm_northing');
						var utc = plots.fieldByName('utc');
						var stakeDeviation = plots.fieldByName('stake_deviation');
						var distanceDeviation = plots.fieldByName('distance_deviation');
						var comments = plots.fieldByName('comments');
						var plotID = plots.fieldByName('plot_id');

						db.execute('INSERT INTO plot (plot_name, utm_zone, utm_easting, utm_northing, utc, stake_deviation, distance_deviation, \
							transect_id, comments) VALUES (?,?,?,?,?,?,?,?,?)', plotName, plotUtmZone, plotUtmEasting, plotUtmNorthing,
							utc, stakeDeviation, distanceDeviation, newTransectID, comments);

						// Get the plot_id for the last row inserted
						results = db.execute('SELECT last_insert_rowid() as plotID');
						var newPlotID = results.fieldByName('plotID');

						// Get any plot observations associated with the plot
						var observations = db.execute('SELECT * FROM plot_observation WHERE plot_id = ?', plotID);

						// Copy and associate any existing plot observations
						while (observations.isValidRow()){
							var observation = observations.fieldByName('observation');
							var groundCover = 0;
							var count = observations.fieldByName('count');
							var observationComments = observations.fieldByName('comments');

							db.execute('INSERT INTO plot_observation (observation, ground_cover, count, comments, plot_id) \
								VALUES (?,?,?,?,?)', observation, groundCover, count, observationComments, newPlotID);

							observations.next();
						}
						plots.next();
					}
					transects.next();
				}
				observations.close();
				plots.close();
				transects.close();
			}

			//Ti.App.fireEvent("app:refreshSiteSurveys");
		} catch (e){
			var errorMessage = e.message;
			Ti.App.fireEvent("app:dataBaseError", {error: errorMessage});
		} finally {
			protocolResult.close();
			parkResult.close();
			previousSurveys.close();
			results.close();
			db.close();
			$.addSiteSurveyWin.close();
		}
	} else {
		alert ('this error should never happen, ever.');
	}
}

/* Everything that follows is search bar related */

// SEARCH BAR ACTIONS

//var last_search = null;
var timers = 0;

//create the popup window to show search results
var win = Ti.UI.createWindow({
	borderColor : "#C0C0C0",
	scrollable : true,
	height: 330,
	left : 200,
	right : 40,
	top : 135,
	borderRadius : 0,
	borderWidth: 3,
	title : 'park names',
	//orientationModes : [Ti.UI.PORTRAIT, Ti.UI.UPSIDE_PORTRAIT]
});

//AUTOCOMPLETE TABLE - list of results from search
var table_data = [];
var autocomplete_table = Titanium.UI.createTableView({
	search : $.parkSrch.value,
	top : 0,
	height: Ti.UI.FILL
});
win.add(autocomplete_table);

//Auto-complete search
function auto_complete(search_term) {
	if (search_term.length >= 1) {
		//clear the table view results
		autocomplete_table.setData([]);
		autocomplete_table.setData(table_data);

		//open database
		try {
			var db = Ti.Database.open('ltemaDB');

			//Query - Retrieve matching park names from database
			rows = db.execute('SELECT park_name ' + 'FROM park ' + 'WHERE park_name LIKE ?', search_term + '%');

			//Ti.API.info(rows.getRowCount());

			//check if any results are returned
			if (rows.getRowCount() <= 0) {
				//TODO: determine if the user can create a new park name, and how to implement
				//for now, the next line is commented out, close() allows the user to enter an invalid park name
				//win.close();
			} else {
				win.open();

				while (rows.isValidRow()) {
					var parkName = rows.fieldByName('park_name');

					//create a new row
					var newRow = Ti.UI.createTableViewRow({
						title : parkName
					});

					//Add row to the table view
					autocomplete_table.appendRow(newRow);
					rows.next();
				}
			}
		} catch (e) {
			var errorMessage = e.message;
			Ti.App.fireEvent("app:dataBaseError", {error: errorMessage});
		} finally {
			rows.close();
			db.close();
		}
	}
}

//Event Listener - when user types in the search bar
$.parkSrch.addEventListener('change', function(e) {
	if (e.source.value.length >= 1 ) { //&& e.source.value != last_search
		win.open();
		clearTimeout(timers['autocomplete']);
		timers['autocomplete'] = setTimeout(function() {
			//last_search = e.source.value;
			auto_complete(e.source.value);
		}, 300);
	} else {
		//if user deletes input
		//clear the table view results
		autocomplete_table.setData([]);
		autocomplete_table.setData(table_data);
		win.close();
	}
	return false;
});

//Event Listener - search results selected by user
autocomplete_table.addEventListener('click', function(e) {
	//add selected park name to the search bar value
	$.parkSrch.value = e.source.title;
	$.parkSrchError.visible = false;
	win.close();
	$.parkSrch.blur();
});
<commit_msg>sanatize addSiteSurvey park name search field<commit_after>/* Site survey creation screen with validation */

// Tabbed Bar Labels
var pickBiomeLabels = [];
var pickProtocolLabels = [];

// Populate the biome TabbedBar with database-derived labels
try {
	var db = Ti.Database.open('ltemaDB');
	var biomeResultRows = db.execute('SELECT biome_name FROM biome ');
	while (biomeResultRows.isValidRow()) {
		var biomeName = biomeResultRows.fieldByName('biome_name');
		pickBiomeLabels.push({title:biomeName, enabled:true});
		biomeResultRows.next();
	}
} catch(e) {
	var errorMessage = e.message;
	Ti.App.fireEvent("app:dataBaseError", {error: errorMessage});
} finally {
	biomeResultRows.close();
	db.close();
	$.pickBiome.labels = pickBiomeLabels;
}

/* Nav Bar Label */

// Build title label

var labelText = 'New Site Survey';

var titleLabel = Titanium.UI.createLabel({
	top:10,
	text: labelText,
	textAlign:'center',
	font:{fontSize:20,fontWeight:'bold'},
});

// Associate label to title
$.addSiteSurveyWin.setTitleControl(titleLabel);

// Instruciton text
var instructions = "Start typing in the search bar to find a park.\n\n" +
					"Picking a Biome will show its Protocols to choose from.\n\n" +
					"Pick a Protocol and click the Done button to create the new site survey.\n\n" +
					"\n" +
					"LTEMA currently supports the Grassland and Alpine protocols.";
$.info.text = instructions;

/* Event Listeners */

// Regenerate protocol TabbedBar based on biome selected
$.pickBiome.addEventListener('click', function(e) {
	//remove old list
	$.pickProtocol.index = -1;
	$.pickBiomeError.visible = false;
	$.pickProtocolError.visible = false;
	while (pickProtocolLabels.length > 0) {
		pickProtocolLabels.pop();
	}
	//populate new list based on new biome selected
	try {
		var db = Ti.Database.open('ltemaDB');
		var protocolResultRows = db.execute('SELECT protocol_name \
											FROM protocol p, biome b \
											WHERE p.biome_id = b.biome_id \
											AND  b.biome_name =?', pickBiomeLabels[e.index].title);
		while (protocolResultRows.isValidRow()) {
			var protocolName = protocolResultRows.fieldByName('protocol_name');
			pickProtocolLabels.push({title:protocolName, enabled:true});
			protocolResultRows.next();
		}
	} catch (e) {
		var errorMessage = e.message;
		Ti.App.fireEvent("app:dataBaseError", {error: errorMessage});
	} finally {
		protocolResultRows.close();
		db.close();
	}

	//refresh TabbedBar
	$.pickProtocol.labels = pickProtocolLabels;

	//TODO: obtain user feedback, disabled for testing
	//auto-select protocol if there's only one
	//if ($.pickProtocol.labels.length == 1) {
	//	$.pickProtocol.index = 0;
	//}
});

// Check for unsupported protocols
$.pickProtocol.addEventListener('click', function(e) {
	if ((pickProtocolLabels[e.index].title !== "Alpine") && (pickProtocolLabels[e.index].title !== "Grassland")) {
		$.pickProtocolError.text = "Unsupported protocol by LTEMA at this time";
		$.pickProtocolError.visible = true;
	} else {
		$.pickProtocolError.visible = false;
	}
});

// Closes the popup result window if user navigates away from this screen
// (improves performance related to issue #14)
$.parkSrch.addEventListener('blur', function(e) {
	win.close();
});

// related to issue #28
$.addSiteSurveyWin.addEventListener('close', function(e) {
	Ti.App.fireEvent("app:refreshSiteSurveys");
});

/* Functions */

//Test for form completeness before adding to database
function doneBtn(e){
	//disable button for 1 second to prevent double entry
	e.source.enabled = false;
	setTimeout(function(){ e.source.enabled = true; },1000);

	var errorFlag = false;
	if (($.parkSrch.value == null) || ($.parkSrch.value == "")) {
		$.parkSrchError.text = "Please select a park";
		$.parkSrchError.visible = true;
		errorFlag = true;
	}
	if ($.pickBiome.index == null) {
		$.pickBiomeError.text = "Please select a biome";
		$.pickBiomeError.visible = true;
		errorFlag - true;
	}
	if (($.pickProtocol.index == null) || ($.pickProtocol.index == -1)) {
		$.pickProtocolError.text = "Please select a protocol";
		$.pickProtocolError.visible = true;
		errorFlag = true;
	} else if (($.pickProtocol.labels[$.pickProtocol.index].title !== "Alpine") && ($.pickProtocol.labels[$.pickProtocol.index].title !== "Grassland")) {
		$.pickProtocolError.text = "Unsupported protocol by LTEMA at this time";
		$.pickProtocolError.visible = true;
		errorFlag = true;
	}

	if (errorFlag === true) {
		return;
	} else if (errorFlag === false) {
		$.parkSrchError.visible = false;
		$.pickBiomeError.visible = false;
		$.pickProtocolError.visible = false;
		try {
			var db = Ti.Database.open('ltemaDB');
			var currentYear = new Date().getFullYear().toString();
			var protocolResult = db.execute('SELECT protocol_id FROM protocol WHERE protocol_name =?', $.pickProtocol.labels[$.pickProtocol.index].title);
			var protocolID = protocolResult.fieldByName('protocol_id');
			var parkResult = db.execute('SELECT park_id FROM park WHERE park_name =?', $.parkSrch.value);
			var parkID = parkResult.fieldByName('park_id');

			// Check if this site has been previously surveyed
			var previousSurveys = db.execute('SELECT site_id FROM site_survey \
											WHERE protocol_id = ? \
											AND park_id = ?', protocolID, parkID);

			// Get the most recently entered survey
			var id;
			while (previousSurveys.isValidRow()) {
				id = previousSurveys.fieldByName('site_id');
				previousSurveys.next();
			}

			// Insert the new survey
			db.execute( 'INSERT INTO site_survey (year, protocol_id, park_id) VALUES (?,?,?)', currentYear, protocolID, parkID);

			//get the id of the last row inserted into the database - *not sure if this is acceptable sql code to use?
			var results = db.execute('SELECT last_insert_rowid() as siteID');
			var siteID = results.fieldByName('siteID');

			// Get the transects associated with the survey
			if (id != null) {
				var transects = db.execute('SELECT * FROM transect WHERE site_id = ?', id);

				// Copy and associate any exiting transects
				while (transects.isValidRow()) {
					var transectName = transects.fieldByName('transect_name');
					var surveyor = transects.fieldByName('surveyor');
					var otherSurveyors = transects.fieldByName('other_surveyors');
					var plotDistance = transects.fieldByName('plot_distance');
					var stakeOrientation = transects.fieldByName('stake_orientation');
					var utmZone = transects.fieldByName('utm_zone');
					var utmEasting = transects.fieldByName('utm_easting');
					var utmNorthing = transects.fieldByName('utm_northing');
					var tComments = transects.fieldByName('comments');
					var transectID = transects.fieldByName('transect_id');

					db.execute('INSERT INTO transect (transect_name, surveyor, other_surveyors, plot_distance, stake_orientation, \
						utm_zone, utm_easting, utm_northing, comments, site_id) \
						VALUES (?,?,?,?,?,?,?,?,?,?)', transectName, surveyor, otherSurveyors, plotDistance, stakeOrientation, utmZone,
						utmEasting, utmNorthing, tComments, siteID);

					// Get the transect_id for the last row inserted
					results = db.execute('SELECT last_insert_rowid() as transectID');
					var newTransectID = results.fieldByName('transectID');

					// Get any plots associated with the transect
					var plots = db.execute('SELECT * FROM plot WHERE transect_id = ?', transectID);

					// Copy and associate any existing plots
					while (plots.isValidRow()) {
						var plotName = plots.fieldByName('plot_name');
						var plotUtmZone = plots.fieldByName('utm_zone');
						var plotUtmEasting = plots.fieldByName('utm_easting');
						var plotUtmNorthing = plots.fieldByName('utm_northing');
						var utc = plots.fieldByName('utc');
						var stakeDeviation = plots.fieldByName('stake_deviation');
						var distanceDeviation = plots.fieldByName('distance_deviation');
						var comments = plots.fieldByName('comments');
						var plotID = plots.fieldByName('plot_id');

						db.execute('INSERT INTO plot (plot_name, utm_zone, utm_easting, utm_northing, utc, stake_deviation, distance_deviation, \
							transect_id, comments) VALUES (?,?,?,?,?,?,?,?,?)', plotName, plotUtmZone, plotUtmEasting, plotUtmNorthing,
							utc, stakeDeviation, distanceDeviation, newTransectID, comments);

						// Get the plot_id for the last row inserted
						results = db.execute('SELECT last_insert_rowid() as plotID');
						var newPlotID = results.fieldByName('plotID');

						// Get any plot observations associated with the plot
						var observations = db.execute('SELECT * FROM plot_observation WHERE plot_id = ?', plotID);

						// Copy and associate any existing plot observations
						while (observations.isValidRow()){
							var observation = observations.fieldByName('observation');
							var groundCover = 0;
							var count = observations.fieldByName('count');
							var observationComments = observations.fieldByName('comments');

							db.execute('INSERT INTO plot_observation (observation, ground_cover, count, comments, plot_id) \
								VALUES (?,?,?,?,?)', observation, groundCover, count, observationComments, newPlotID);

							observations.next();
						}
						plots.next();
					}
					transects.next();
				}
				observations.close();
				plots.close();
				transects.close();
			}

			//Ti.App.fireEvent("app:refreshSiteSurveys");
		} catch (e){
			var errorMessage = e.message;
			Ti.App.fireEvent("app:dataBaseError", {error: errorMessage});
		} finally {
			protocolResult.close();
			parkResult.close();
			previousSurveys.close();
			results.close();
			db.close();
			$.addSiteSurveyWin.close();
		}
	} else {
		alert ('this error should never happen, ever.');
	}
}

/* Everything that follows is search bar related */

// SEARCH BAR ACTIONS

//var last_search = null;
var timers = 0;

//create the popup window to show search results
var win = Ti.UI.createWindow({
	borderColor : "#C0C0C0",
	scrollable : true,
	height: 330,
	left : 200,
	right : 40,
	top : 135,
	borderRadius : 0,
	borderWidth: 3,
	title : 'park names',
	//orientationModes : [Ti.UI.PORTRAIT, Ti.UI.UPSIDE_PORTRAIT]
});

//AUTOCOMPLETE TABLE - list of results from search
var table_data = [];
var autocomplete_table = Titanium.UI.createTableView({
	search : $.parkSrch.value,
	top : 0,
	height: Ti.UI.FILL
});
win.add(autocomplete_table);

//Auto-complete search
function auto_complete(search_term) {
	if (search_term.length >= 1) {
		//clear the table view results
		autocomplete_table.setData([]);
		autocomplete_table.setData(table_data);

		//open database
		try {
			var db = Ti.Database.open('ltemaDB');

			//Query - Retrieve matching park names from database
			rows = db.execute('SELECT park_name ' + 'FROM park ' + 'WHERE park_name LIKE ?', search_term + '%');

			//Ti.API.info(rows.getRowCount());

			//check if any results are returned
			if (rows.getRowCount() <= 0) {
				//TODO: determine if the user can create a new park name, and how to implement
				//for now, the next line is commented out, close() allows the user to enter an invalid park name
				//win.close();
			} else {
				win.open();

				while (rows.isValidRow()) {
					var parkName = rows.fieldByName('park_name');

					//create a new row
					var newRow = Ti.UI.createTableViewRow({
						title : parkName
					});

					//Add row to the table view
					autocomplete_table.appendRow(newRow);
					rows.next();
				}
			}
		} catch (e) {
			var errorMessage = e.message;
			Ti.App.fireEvent("app:dataBaseError", {error: errorMessage});
		} finally {
			rows.close();
			db.close();
		}
	}
}

//Event Listener - when user types in the search bar
$.parkSrch.addEventListener('change', function(e) {
	var match = /^[A-Za-z]/;  //santatize search input by reqauiring a letter
	if ((e.source.value.length < 1) || (!e.source.value.match(match)) ) {
		autocomplete_table.setData([]);
		autocomplete_table.setData(table_data);
		win.close();
	} else {
		win.open();
		clearTimeout(timers['autocomplete']);
		timers['autocomplete'] = setTimeout(function() {
			//last_search = e.source.value;
			auto_complete(e.source.value);
		}, 300);
		//if user deletes input
		//clear the table view results

	}
	return false;
});

//Event Listener - search results selected by user
autocomplete_table.addEventListener('click', function(e) {
	//add selected park name to the search bar value
	$.parkSrch.value = e.source.title;
	$.parkSrchError.visible = false;
	win.close();
	$.parkSrch.blur();
});
<|endoftext|>