<commit_before>/**
 * The Api class provides a simple interface to all functions the ToureNPlaner server provides.
 */
window.Api = function(attributes) {
    var defaults;

    attributes = attributes || {};
    if ((defaults = this.defaults))
        attributes = _.extend({}, defaults, attributes);

    this.attributes = {};
    this.set(attributes);
};

_.extend(window.Api.prototype, {
    /* The default configuration */
    defaults: {
        authAsBase64 : '', // provides username+password Base64 encoded
        authRequired : false,
        server : 'localhost', // url of the server
        port : 8081, // port on which the server listens
        ssl : false,
        realm : 'Tourenplaner'
    },

    /**
     * Change one or multiple configuration variables.
     *
     * @param attrs Key-Value object of configuration variables
     * @return The instance of the class
     */
    set: function(attrs) {
        if (!attrs)
            return this;

        for (var attr in attrs)
            this.attributes[attr] = attrs[attr];

        return this;
    },

    /**
     * Get a configuration variable by name.
     *
     * @param attr The name of the configuration variable
     * @return The value of the variable
     */
    get: function(attr) {
        return this.attributes[attr];
    },

    /**
     * Sends the given request to the server. May include an Authorization header if necessary.
     *
     * @param reqData.type The HTTP request type, e.g. POST or GET
     * @param reqData.suffix The suffix used for the requested API method
     * @param reqData.request String/Object containing the request
     * @param reqData.callback Callback function that gets called once the request returns
     * @return False if the reqData object was incorrect
     */
    send : function (reqData) {
        var url = "", event = {};
        if (_.isUndefined(reqData) || _.isNull(reqData)) return false;
        if (_.isUndefined(reqData.suffix) || !reqData.suffix) return false;
        if (_.isNull(reqData.process) || _.isUndefined(reqData.process)) reqData.process = true;
        reqData.type = reqData.type || 'GET';
        reqData.request = reqData.request || {};

        if (!_.isNull(this.get('server')) && !_.isUndefined(this.get('server'))) {
            url +=  (this.get('ssl') ? 'https://' : 'http://') +
                    this.get('server') + ':' +
                    (!_.isNaN(this.get('port')) ? this.get('port') : 80);
        }
        url += '/' + reqData.suffix;

        // init event for events and bind callback to it
        _.extend(event, Backbone.Events);
        if (_.isFunction(reqData.callback))
            event.bind('request', reqData.callback);

        // method performs an asyncronous HTTP request with or without
        // authentication.
        // performs a callback, successful or not
        var headers = this.get('authRequired') && !_.isEmpty(this.get('authAsBase64')) ? { Authorization: this.get('realm') + ' ' + this.get('authAsBase64')} : {};
        var that = this;
        $.ajax(url, {
            cache: false,
            type: reqData.type,
            accepts: 'json',
            dataType: 'json',
            contentType: 'application/json; charset="utf-8"',
            crossDomain: true,
            data: reqData.type == 'POST' ? JSON.stringify(reqData.request) : reqData.request,
            processData: reqData.process,
            headers: headers,
            success: function (data, textStatus, jqXHR) {
                var obj = jqXHR.responseText;
                if (_.isString(obj) && obj != '')
                    obj = JSON.parse(obj);
                event.trigger('request', obj, true);
            },
            error: function (jqXHR, textStatus, errorThrown) {
                var text = jqXHR.responseText || "";

                // use simple error if response is empty
                if (text === "") {
                    if(errorThrown !== "")
                        text = errorThrown;
                    else
                        text = that.get('error').message + ": " + that.get('error').details; // if everything is empty use standard error
                } else {
                    text = JSON.parse(text);
                }

                event.trigger('request', text, false);

                // Also display an error message for the user
                if (!reqData.silent && (_.isUndefined(window.QUnit) && MessageView)) {
                    var message = text;
                    if (!_.isString(text))
                        message = text.message + ': ' + text.details;
                    new MessageView({title: $._("Error"), message: message}).render();
                }
            }
        });

        return true;
    },

    /**
     * Retrieve the server information object from the server. Automatically sets some configuration variables for the api.
     *
     * @param args.callback Callback function that gets called after the request returns
     * @param args.callback Callback function which will be called after the request returns
     * @return True if the arguments were correct, false otherwise
     */
    serverInformation : function (args) {
        var that = this;
        return this.send({
            suffix : 'info',
            request : {},
            callback: function (text, success) {
                if(text.servertype == "private")
                    that.set({'ssl': true, 'port': text.sslport, 'authRequired': true});
                if (_.isFunction(args.callback))
                    args.callback(text, success);
            }
        });
    },

    /**
     * Creates a new user account on the server.
     *
     * @param args.userObject Object containing the necessary infos to create a user
     * @param args.callback Callback function which will be called after the request returns
     * @return True if the arguments were correct, false otherwise
     */
    registerUser: function (args) {
        if (!args || !args.userObject)
            return false;
        this.send({
            type : 'POST',
            suffix : 'registeruser',
            request : args.userObject,
            callback: _.isFunction(args.callback) ? args.callback : null
        });

        return true;
    },

    /**
     * Checks the email/password combination with the server.
     *
     * @param args.email Emailaddress of the user
     * @param args.password Password of the user
     * @param args.callback Callback function which will be called after the request returns
     * @return True if the arguments were correct, false otherwise
     */
    authUser : function (args) {
        if (!args || !args.email || !args.password)
            return false;

        this.set({'authAsBase64': Base64.encode(args.email + ':' + args.password)});

        this.send({
            silent: true,
            suffix: 'authuser',
            callback: _.isFunction(args.callback) ? args.callback : null
        });

        return true;
    },

    /**
     * Remove all login informations.
     */
    logout : function () {
        this.set({'authAsBase64': ''});
    },

    /**
     * Get user data of the given id or the own data.
     *
     * @param args.id The id of the user for which to get the data (optional)
     * @param args.callback Callback function which will be called after the request returns
     * @return True if the arguments were correct, false otherwise
     */
    getUser : function (args) {
        var suffix = 'getuser';
        if (args.id && !_.isNaN(args.id))
            suffix += '?id=' + args.id;

        this.send({
            suffix : suffix,
            request : '',
            callback : _.isFunction(args.callback) ? args.callback : null
        });
        return true;
    },

    /**
     * Change the user data of the given id or the data of the logged in user.
     *
     * @param args.id The id of the user for which to change the data (optional)
     * @param args.userObject The new user data
     * @param args.callback Callback function which will be called after the request returns
     * @return True if the arguments were correct, false otherwise
     */
    updateUser : function (args) {
        if (!args || !args.userObject)
            return false;

        var suffix = 'updateuser';
        if (args.id && !_.isNaN(args.id))
            suffix += '?id=' + args.id;

        this.send({
            type : 'POST',
            suffix : suffix,
            request : args.userObject,
            callback : _.isFunction(args.callback) ? args.callback : null
        });

        return true;
    },

    /**
     * Retrieves a list of all requests of every user or of a single, specified user.
     *
     * @param args.id The id of the single user (optional)
     * qparam args.limit The max. number of items retrieved
     * @param args.offset The offset of the first retrieved item
     * @param args.callback Callback function which will be called after the request returns
     * @return True if the arguments were correct, false otherwise
     */
    listRequests : function (args) {
        if (!args || args.offset<0 || args.limit<0 || isNaN(args.offset) || isNaN(args.limit))
            return false;

        var suffix = 'listrequests?limit=' + args.limit + '&offset=' + args.offset;
        if (args.id && !isNaN(args.id))
            suffix += '&ID=' + args.id;

        this.send({
            type : 'POST',
            suffix : suffix,
            request : '',
            callback : _.isFunction(args.callback) ? args.callback : null
        });

        return true;
    },

    /**
     * Retrieves a list of all registered users on the server.
     *
     * @param args.limit The max. numbers of users retrieved
     * @param args.offset The offset of the first retrieved user
     * @param args.callback Callback function which will be called after the request returns
     * @return True if the arguments were correct, false otherwise
     */
    listUsers : function (args) {
        if (!args || isNaN(args.offset) || isNaN(args.limit) || args.offset<0 || args.limit<0)
            return false;
        this.send({
            suffix : 'listusers?limit=' + args.limit + '&offset=' + args.offset,
            request : '',
            callback : _.isFunction(args.callback) ? args.callback : null
        });

        return true;
    },

    /**
     * Deletes the specified user on the server.
     *
     * @param args.id The id of the user that should be deleted
     * @param args.callback Callback function which will be called after the request returns
     * @return True if the arguments were correct, false otherwise
     */
    deleteUser : function (args) {
        if (!args || !args.id || isNaN(args.id))
            return false;
        this.send({
            suffix : 'deleteuser?id=' + args.id,
            request : '',
            callback : _.isFunction(args.callback) ? args.callback : null
        });

        return true;
    },

    /**
     * Requests the nearest valid neighbouring point on a street for the given point.
     *
     * @param args.points A list of points for which neighbours should be searched
     * @param args.callback Callback function which will be called after the request returns
     * @return True if the arguments were correct, false otherwise
     */
    nearestNeighbour : function (args) {
        if (!args || !args.points)
            return false;
        this.send({
            suffix : 'algnns',
            type : 'POST',
            request : {
                version: 1,
                points: _.isArray(args.points) ? args.points : [args.points]
            },
            callback : _.isFunction(args.callback) ? args.callback : null
        });

        return true;
    },

    /**
     * Sends an algorithm calculation request to the server.
     *
     * Use either args.request or args.point, args.version and args.contraints.
     *
     * @param args.alg The shortname of the desired algorithm
     * @param args.request A complete request object according to the specification
     * @param args.points The points supplied to the algorithm
     * @param args.version The requested version of the algorithm
     * @param args.constraints Constraints that need to be applied to the algorithm
     * @param args.callback Callback function which will be called after the request returns
     * @return True if the arguments were correct, false otherwise
     */
    alg : function (args) {
        var thisrequest = {};
        if (!args || !args.alg)
            return false;
        // use given request or make own
        if (args.request) {
            if (!args.request.points)
                return false;
            else
                thisrequest = args.request;

        } else {
            if (!args.points)
                return false;
            thisrequest = {
                version: args.version || 1,
                points: args.points,
                constraints: args.constraints || {}
            };
        }
        this.send({
            type : 'POST',
            suffix : 'alg' + args.alg,
            request : thisrequest,
            process: false,
            callback : _.isFunction(args.callback) ? args.callback : null
        });

        return true;
    },

    /**
     * Retrieve a single request from the server.
     *
     * @param args.id The id of the request
     * @param args.callback Callback function which will be called after the request returns
     * @return True if the arguments were correct, false otherwise
     */
    getRequest: function(args) {
        if (!args || !args.id)
            return false;

        this.send({
            suffix: 'getrequest?id=' + args.id,
            request: '',
            callback: _.isFunction(args.callback) ? args.callback : null
        });

        return true;
    },

    /**
     * Retrieve a single response from the server.
     *
     * @param args.id The id of the response
     * @param args.callback Callback function which will be called after the request returns
     * @return True if the arguments were correct, false otherwise
     */
    getResponse: function(args) {
        if (!args || !args.id)
            return false;

        this.send({
            suffix: 'getresponse?id=' + args.id,
            request: '',
            callback: _.isFunction(args.callback) ? args.callback : null
        });

        return true;
    }
});
<commit_msg>fixed missing changes for #116<commit_after>/**
 * The Api class provides a simple interface to all functions the ToureNPlaner server provides.
 */
window.Api = function(attributes) {
    var defaults;

    attributes = attributes || {};
    if ((defaults = this.defaults))
        attributes = _.extend({}, defaults, attributes);

    this.attributes = {};
    this.set(attributes);
};

_.extend(window.Api.prototype, {
    /* The default configuration */
    defaults: {
        authAsBase64 : '', // provides username+password Base64 encoded
        authRequired : false,
        server : 'localhost', // url of the server
        port : 8081, // port on which the server listens
        ssl : false,
        realm : 'Tourenplaner'
    },

    /**
     * Change one or multiple configuration variables.
     *
     * @param attrs Key-Value object of configuration variables
     * @return The instance of the class
     */
    set: function(attrs) {
        if (!attrs)
            return this;

        for (var attr in attrs)
            this.attributes[attr] = attrs[attr];

        return this;
    },

    /**
     * Get a configuration variable by name.
     *
     * @param attr The name of the configuration variable
     * @return The value of the variable
     */
    get: function(attr) {
        return this.attributes[attr];
    },

    /**
     * Sends the given request to the server. May include an Authorization header if necessary.
     *
     * @param reqData.type The HTTP request type, e.g. POST or GET
     * @param reqData.suffix The suffix used for the requested API method
     * @param reqData.request String/Object containing the request
     * @param reqData.callback Callback function that gets called once the request returns
     * @return False if the reqData object was incorrect
     */
    send : function (reqData) {
        var url = "", event = {};
        if (_.isUndefined(reqData) || _.isNull(reqData)) return false;
        if (_.isUndefined(reqData.suffix) || !reqData.suffix) return false;
        if (_.isNull(reqData.process) || _.isUndefined(reqData.process)) reqData.process = true;
        reqData.type = reqData.type || 'GET';
        reqData.request = reqData.request || {};

        if (!_.isNull(this.get('server')) && !_.isUndefined(this.get('server'))) {
            url +=  (this.get('ssl') ? 'https://' : 'http://') +
                    this.get('server') + ':' +
                    (!_.isNaN(this.get('port')) ? this.get('port') : 80);
        }
        url += '/' + reqData.suffix;

        // init event for events and bind callback to it
        _.extend(event, Backbone.Events);
        if (_.isFunction(reqData.callback))
            event.bind('request', reqData.callback);

        // method performs an asyncronous HTTP request with or without
        // authentication.
        // performs a callback, successful or not
        var headers = this.get('authRequired') && !_.isEmpty(this.get('authAsBase64')) ? { Authorization: this.get('realm') + ' ' + this.get('authAsBase64')} : {};
        var that = this;
        $.ajax(url, {
            cache: false,
            type: reqData.type,
            accepts: 'json',
            dataType: 'json',
            contentType: 'application/json; charset="utf-8"',
            crossDomain: true,
            data: reqData.type == 'POST' ? JSON.stringify(reqData.request) : reqData.request,
            processData: reqData.process,
            headers: headers,
            success: function (data, textStatus, jqXHR) {
                var obj = jqXHR.responseText;
                if (_.isString(obj) && obj != '')
                    obj = JSON.parse(obj);
                event.trigger('request', obj, true);
            },
            error: function (jqXHR, textStatus, errorThrown) {
                var text = jqXHR.responseText || "";

                // use simple error if response is empty
                if (text === "") {
                    if(errorThrown !== "")
                        text = errorThrown;
                    else
                        text = that.get('error').message + ": " + that.get('error').details; // if everything is empty use standard error
                } else {
                    text = JSON.parse(text);
                }

                event.trigger('request', text, false);

                // Also display an error message for the user
                if (!reqData.silent && (_.isUndefined(window.QUnit) && MessageView)) {
                    var message = text;
                    if (!_.isString(text))
                        message = text.message + ': ' + text.details;
                    new MessageView({title: $._("Error"), message: message}).render();
                }
            }
        });

        return true;
    },

    /**
     * Retrieve the server information object from the server. Automatically sets some configuration variables for the api.
     *
     * @param args.callback Callback function that gets called after the request returns
     * @param args.callback Callback function which will be called after the request returns
     * @return True if the arguments were correct, false otherwise
     */
    serverInformation : function (args) {
        var that = this;
        return this.send({
            suffix : 'info',
            request : {},
            callback: function (text, success) {
                if(text.servertype == "private")
                    that.set({'ssl': true, 'port': text.sslport, 'authRequired': true});
                if (_.isFunction(args.callback))
                    args.callback(text, success);
            }
        });
    },

    /**
     * Creates a new user account on the server.
     *
     * @param args.userObject Object containing the necessary infos to create a user
     * @param args.callback Callback function which will be called after the request returns
     * @return True if the arguments were correct, false otherwise
     */
    registerUser: function (args) {
        if (!args || !args.userObject)
            return false;
        this.send({
            type : 'POST',
            suffix : 'registeruser',
            request : args.userObject,
            callback: _.isFunction(args.callback) ? args.callback : null
        });

        return true;
    },

    /**
     * Checks the email/password combination with the server.
     *
     * @param args.email Emailaddress of the user
     * @param args.password Password of the user
     * @param args.callback Callback function which will be called after the request returns
     * @return True if the arguments were correct, false otherwise
     */
    authUser : function (args) {
        if (!args || !args.email || !args.password)
            return false;

        this.set({'authAsBase64': Base64.encode(args.email + ':' + args.password)});

        this.send({
            silent: true,
            suffix: 'authuser',
            callback: _.isFunction(args.callback) ? args.callback : null
        });

        return true;
    },

    /**
     * Remove all login informations.
     */
    logout : function () {
        this.set({'authAsBase64': ''});
    },

    /**
     * Get user data of the given id or the own data.
     *
     * @param args.id The id of the user for which to get the data (optional)
     * @param args.callback Callback function which will be called after the request returns
     * @return True if the arguments were correct, false otherwise
     */
    getUser : function (args) {
        var suffix = 'getuser';
        if (args.id && !_.isNaN(args.id))
            suffix += '?id=' + args.id;

        this.send({
            suffix : suffix,
            request : '',
            callback : _.isFunction(args.callback) ? args.callback : null
        });
        return true;
    },

    /**
     * Change the user data of the given id or the data of the logged in user.
     *
     * @param args.id The id of the user for which to change the data (optional)
     * @param args.userObject The new user data
     * @param args.callback Callback function which will be called after the request returns
     * @return True if the arguments were correct, false otherwise
     */
    updateUser : function (args) {
        if (!args || !args.userObject)
            return false;

        var suffix = 'updateuser';
        if (args.id && !_.isNaN(args.id))
            suffix += '?id=' + args.id;

        this.send({
            type : 'POST',
            suffix : suffix,
            request : args.userObject,
            callback : _.isFunction(args.callback) ? args.callback : null
        });

        return true;
    },

    /**
     * Retrieves a list of all requests of every user or of a single, specified user.
     *
     * @param args.id The id of the single user (optional)
     * qparam args.limit The max. number of items retrieved
     * @param args.offset The offset of the first retrieved item
     * @param args.callback Callback function which will be called after the request returns
     * @return True if the arguments were correct, false otherwise
     */
    listRequests : function (args) {
        if (!args || args.offset<0 || args.limit<0 || isNaN(args.offset) || isNaN(args.limit))
            return false;

        var suffix = 'listrequests?limit=' + args.limit + '&offset=' + args.offset;
        if (args.id && !isNaN(args.id))
            suffix += '&id=' + args.id;
        else if(window.app.user.get("admin"))
            suffix += '&id=all';

        this.send({
            type : 'POST',
            suffix : suffix,
            request : '',
            callback : _.isFunction(args.callback) ? args.callback : null
        });

        return true;
    },

    /**
     * Retrieves a list of all registered users on the server.
     *
     * @param args.limit The max. numbers of users retrieved
     * @param args.offset The offset of the first retrieved user
     * @param args.callback Callback function which will be called after the request returns
     * @return True if the arguments were correct, false otherwise
     */
    listUsers : function (args) {
        if (!args || isNaN(args.offset) || isNaN(args.limit) || args.offset<0 || args.limit<0)
            return false;
        this.send({
            suffix : 'listusers?limit=' + args.limit + '&offset=' + args.offset,
            request : '',
            callback : _.isFunction(args.callback) ? args.callback : null
        });

        return true;
    },

    /**
     * Deletes the specified user on the server.
     *
     * @param args.id The id of the user that should be deleted
     * @param args.callback Callback function which will be called after the request returns
     * @return True if the arguments were correct, false otherwise
     */
    deleteUser : function (args) {
        if (!args || !args.id || isNaN(args.id))
            return false;
        this.send({
            suffix : 'deleteuser?id=' + args.id,
            request : '',
            callback : _.isFunction(args.callback) ? args.callback : null
        });

        return true;
    },

    /**
     * Requests the nearest valid neighbouring point on a street for the given point.
     *
     * @param args.points A list of points for which neighbours should be searched
     * @param args.callback Callback function which will be called after the request returns
     * @return True if the arguments were correct, false otherwise
     */
    nearestNeighbour : function (args) {
        if (!args || !args.points)
            return false;
        this.send({
            suffix : 'algnns',
            type : 'POST',
            request : {
                version: 1,
                points: _.isArray(args.points) ? args.points : [args.points]
            },
            callback : _.isFunction(args.callback) ? args.callback : null
        });

        return true;
    },

    /**
     * Sends an algorithm calculation request to the server.
     *
     * Use either args.request or args.point, args.version and args.contraints.
     *
     * @param args.alg The shortname of the desired algorithm
     * @param args.request A complete request object according to the specification
     * @param args.points The points supplied to the algorithm
     * @param args.version The requested version of the algorithm
     * @param args.constraints Constraints that need to be applied to the algorithm
     * @param args.callback Callback function which will be called after the request returns
     * @return True if the arguments were correct, false otherwise
     */
    alg : function (args) {
        var thisrequest = {};
        if (!args || !args.alg)
            return false;
        // use given request or make own
        if (args.request) {
            if (!args.request.points)
                return false;
            else
                thisrequest = args.request;

        } else {
            if (!args.points)
                return false;
            thisrequest = {
                version: args.version || 1,
                points: args.points,
                constraints: args.constraints || {}
            };
        }
        this.send({
            type : 'POST',
            suffix : 'alg' + args.alg,
            request : thisrequest,
            process: false,
            callback : _.isFunction(args.callback) ? args.callback : null
        });

        return true;
    },

    /**
     * Retrieve a single request from the server.
     *
     * @param args.id The id of the request
     * @param args.callback Callback function which will be called after the request returns
     * @return True if the arguments were correct, false otherwise
     */
    getRequest: function(args) {
        if (!args || !args.id)
            return false;

        this.send({
            suffix: 'getrequest?id=' + args.id,
            request: '',
            callback: _.isFunction(args.callback) ? args.callback : null
        });

        return true;
    },

    /**
     * Retrieve a single response from the server.
     *
     * @param args.id The id of the response
     * @param args.callback Callback function which will be called after the request returns
     * @return True if the arguments were correct, false otherwise
     */
    getResponse: function(args) {
        if (!args || !args.id)
            return false;

        this.send({
            suffix: 'getresponse?id=' + args.id,
            request: '',
            callback: _.isFunction(args.callback) ? args.callback : null
        });

        return true;
    }
});
<|endoftext|>


<commit_before>/*global angular, DocumentTouch*/

(function () {
  'use strict';

  var magnify = angular.module('ngMagnify', []);

  magnify.directive('ngMagnify', function () {
    return {
      restrict: 'EA',
      replace: true,
      template: '
' +
                  '

' +
                  '' +
                '
',
      scope: {
        imageSrc: '@',
        imageWidth: '=',
        imageHeight: '=',
        glassWidth: '=',
        glassHeight: '='
      },
      link: function (scope, element) {
        var glass = element.find('div'),
          image = element.find('img'),
          el, nWidth, nHeight, magnifyCSS;

        // if touch devices, do something
        if (('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) {
          return;
        }
        element.on('mouseenter', function () {
          el = angular.extend(scope.getOffset(element[0]), {
            width: element[0].offsetWidth,
            height: element[0].offsetHeight,
            imageWidth: image[0].offsetWidth,
            imageHeight: image[0].offsetHeight,
            glassWidth: glass[0].offsetWidth,
            glassHeight: glass[0].offsetHeight
          });
        })
        .on('mousemove', function (evt) {
          magnifyCSS = scope.magnify(evt);

          if (magnifyCSS) {
            glass.css( magnifyCSS );
          }
        })
        .on('mouseout', function () {
          glass.on('mouseleave', function () {
            glass.css({
              opacity: 0,
              filter: 'alpha(opacity=0)'
            });
          });
        });

        scope.magnify = function (evt) {
          var mx, my, rx, ry, px, py, bgp, img;

          if (!nWidth && !nHeight) {
            img = new Image();
            img.onload = function () {
              nWidth = img.width;
              nHeight = img.height;
            };
            img.src = scope.imageSrc;
          } else {
            // IE8 uses evt.x and evt.y
            mx = (evt.pageX) ? (evt.pageX - el.left) : evt.x;
            my = (evt.pageY) ? (evt.pageY - el.top) : evt.y;

            if (mx < el.width && my < el.height && mx > 0 && my > 0) {
              glass.css({
                opacity: 1,
                'z-index': 1,
                filter: 'alpha(opacity=100)'
              });
            } else {
              glass.css({
                opacity: 0,
                'z-index': -1,
                filter: 'alpha(opacity=0)'
              });
              return;
            }

            rx = Math.round(mx/el.imageWidth*nWidth - el.glassWidth/2)*-1;
            ry = Math.round(my/el.imageHeight*nHeight - el.glassHeight/2)*-1;
            bgp = rx + 'px ' + ry + 'px';

            px = mx - el.glassWidth/2;
            py = my - el.glassHeight/2;

            return { left: px+'px', top: py+'px', backgroundPosition: bgp };
          }
          return;
        };

        scope.getOffset = function (_el) {
          var de = document.documentElement;
          var box = _el.getBoundingClientRect();
          var top = box.top + window.pageYOffset - de.clientTop;
          var left = box.left + window.pageXOffset - de.clientLeft;
          return { top: top, left: left };
        };

        scope.getContainerStyle = function () {
          return {
            width: (scope.imageWidth) ? scope.imageWidth + 'px' : '',
            height: (scope.imageHeight) ? scope.imageHeight + 'px' : ''
          };
        };

        scope.getGlassStyle = function () {
          return {
            background: 'url(' + scope.imageSrc + ') no-repeat',
            width: (scope.glassWidth) ? scope.glassWidth + 'px' : '',
            height: (scope.glassHeight) ? scope.glassHeight + 'px' : ''
          };
        };
      }
    };
  });
})();
<commit_msg>moving offset update to move event to ensure the magnifying glass is positioned correctly when scrolling while the magnifier is drawn, without needing to exit and reenter<commit_after>/*global angular, DocumentTouch*/

(function () {
  'use strict';

  var magnify = angular.module('ngMagnify', []);

  magnify.directive('ngMagnify', function () {
    return {
      restrict: 'EA',
      replace: true,
      template: '
' +
                  '

' +
                  '' +
                '
',
      scope: {
        imageSrc: '@',
        imageWidth: '=',
        imageHeight: '=',
        glassWidth: '=',
        glassHeight: '='
      },
      link: function (scope, element) {
        var glass = element.find('div'),
          image = element.find('img'),
          el, nWidth, nHeight, magnifyCSS;

        // if touch devices, do something
        if (('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) {
          return;
        }
        element.on('mousemove', function (evt) {
          el = angular.extend(scope.getOffset(element[0]), {
            width: element[0].offsetWidth,
            height: element[0].offsetHeight,
            imageWidth: image[0].offsetWidth,
            imageHeight: image[0].offsetHeight,
            glassWidth: glass[0].offsetWidth,
            glassHeight: glass[0].offsetHeight
          });
          magnifyCSS = scope.magnify(evt);

          if (magnifyCSS) {
            glass.css( magnifyCSS );
          }
        })
        .on('mouseout', function () {
          glass.on('mouseleave', function () {
            glass.css({
              opacity: 0,
              filter: 'alpha(opacity=0)'
            });
          });
        });

        scope.magnify = function (evt) {
          var mx, my, rx, ry, px, py, bgp, img;

          if (!nWidth && !nHeight) {
            img = new Image();
            img.onload = function () {
              nWidth = img.width;
              nHeight = img.height;
            };
            img.src = scope.imageSrc;
          } else {
            // IE8 uses evt.x and evt.y
            mx = (evt.pageX) ? (evt.pageX - el.left) : evt.x;
            my = (evt.pageY) ? (evt.pageY - el.top) : evt.y;

            if (mx < el.width && my < el.height && mx > 0 && my > 0) {
              glass.css({
                opacity: 1,
                'z-index': 1,
                filter: 'alpha(opacity=100)'
              });
            } else {
              glass.css({
                opacity: 0,
                'z-index': -1,
                filter: 'alpha(opacity=0)'
              });
              return;
            }

            rx = Math.round(mx/el.imageWidth*nWidth - el.glassWidth/2)*-1;
            ry = Math.round(my/el.imageHeight*nHeight - el.glassHeight/2)*-1;
            bgp = rx + 'px ' + ry + 'px';

            px = mx - el.glassWidth/2;
            py = my - el.glassHeight/2;

            return { left: px+'px', top: py+'px', backgroundPosition: bgp };
          }
          return;
        };

        scope.getOffset = function (_el) {
          var de = document.documentElement;
          var box = _el.getBoundingClientRect();
          var top = box.top + window.pageYOffset - de.clientTop;
          var left = box.left + window.pageXOffset - de.clientLeft;
          return { top: top, left: left };
        };

        scope.getContainerStyle = function () {
          return {
            width: (scope.imageWidth) ? scope.imageWidth + 'px' : '',
            height: (scope.imageHeight) ? scope.imageHeight + 'px' : ''
          };
        };

        scope.getGlassStyle = function () {
          return {
            background: 'url(' + scope.imageSrc + ') no-repeat',
            width: (scope.glassWidth) ? scope.glassWidth + 'px' : '',
            height: (scope.glassHeight) ? scope.glassHeight + 'px' : ''
          };
        };
      }
    };
  });
})();
<|endoftext|>


<commit_before>/*
 * gui-builder - A simple WYSIWYG HTML5 app creator
 * Copyright (c) 2011, Intel Corporation.
 *
 * This program is licensed under the terms and conditions of the
 * Apache License, version 2.0.  The full text of the Apache License is at
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 */
"use strict";

function propertyValueChanged(event) {
    var updated = event.srcElement.id.replace(/-value/,''),
        node = event.data,
        value = null;

    if (node === null || node === undefined) {
        console.error('Missing node data, property change failed!');
        return;
    }

    switch (BWidget.getPropertyType(node.getType(), updated)) {
        case 'boolean':
            value = Boolean($('#' + event.srcElement.id).val());
            break;
        case 'float':
            value = parseFloat($('#' + event.srcElement.id).val());
            break;
        case 'integer':
            value = parseInt($('#' + event.srcElement.id).val(), 10);
            break;
        case 'number':
            value = Number($('#' + event.srcElement.id).val());
            break;
        case 'object':
            value = Object($('#' + event.srcElement.id).val());
            break;
        case 'string':
            value = String($('#' + event.srcElement.id).val());
            break;
        default:
            break;
    }
    console.log(updated + " changed to " + value);
    node.setProperty(updated, value);
    event.node = node;
    showProperties(event);
}

function showProperties(event) {
        var node = event.node,
            showUid = false,
            labelId, labelVal, valueId, valueVal;

        // Clear the properties pane when nothing is selected
        if (node === null || node === undefined) {
            node = ADM.getActivePage();
            if (node === null || node === undefined) {
                $('#property_content').empty()
                    .append('Nothing Selected');
                return;
            }
        }

        console.log("User selected ADMNode_" + node._uid);

        $('#property_content').empty()
            .append('

' +
                    '' +
                    BWidget.getDisplayLabel(node.getType()) +
                    '' +
                    (showUid ? ' (uid=' + node.getUid() + ')' : '') +
                    '

');

        var props = node.getProperties();
        var options = node.getPropertyOptions();
        for (var p in props) {
            if (p == "type_label" || p == "conditional_label" || p == "conditional_for") {
                continue;
            }
            else if (p == "type") {
                labelVal = node.getProperty("type_label");
            }
            else if (p == "conditional") {
                if(node.getProperty("conditional_for") != node.getProperty("type")) {
                    continue;
                }
                labelVal = node.getProperty("conditional_label");
            }
            else {
                labelVal = p.replace(/_/g,'-');
            }
            labelId = p+'-label';
            valueId = p+'-value';
            valueVal = props[p];
            if(options[p]) {
                var code = '
' + labelVal + '	' +
                         '' +
                         '<select id="' + valueId + '">';
                for (var o in options[p]) {
                    if(options[p][o] == props[p]) {
                        code += '<option value="' + options[p][o] + '" selected=true>' + options[p][o] + '</option>';
                    }
                    else {
                        code += '<option value="' + options[p][o] + '">' + options[p][o] + '</option>';
                    }
                }
                code += '</select>' + '
';
                    $('#property_content').children().last()
                        .append(code);
            }
            else {
                $('#property_content').children().last()
                    .append('
' + labelVal + '	' +
                            '' +
                            '<input class="full" id="' + valueId + '" value="'+valueVal + '" />' +
                            '
');
            }

/*
            $('#select').change( function(event) {
                alert("change");
            });
            $('#select').select( function(event) {
                alert("select");
            });
*/

            $('#' + valueId).change( node, propertyValueChanged );
        }

        $('table','#property_content').attr({ 'cellspacing': '0' });
        $('th:odd, td:odd','#property_content')
            .css({ 'border-top': '1px solid black',
                   'border-bottom': '1px solid black' });
        $('td','#property_content').attr('align','left');
        $('th','#property_content').attr('align','left')
            .addClass('ui-helper-reset ui-state-default');

/*
 * The above should produce something like the following so that it will
 * render as a jQ-UI "tab" object
 *

Properties

Inherited

Custom

Inherited properties go here

Custom properties go here

*/
}

function loadProperties(container) {
    var defaultContainer = '#properties-panel',
        myContainer = container,
        contents;

    if (!myContainer) {
        myContainer = $(defaultContainer);
    }

    if (!myContainer || !myContainer.get()) {
        return false;
    }

    // FIXME: There was a
 here to partially make up for an overlap
    //        issue that appears to be caused by the interaction of jQuery UI
    //        accordion and flex box implementation. The palette wants to
    //        draw over the top of the properties pane, by the height of the
    //        Palette header, which leads me to believe it's calculating its
    //        height from its bottom to the top of its parent. I replaced
    //        this
 with a tweak to the CSS margin of palette-panel.
    myContainer.append('
Properties
')
        .children().last()
            .addClass("ui-helper-reset ui-widget ui-widget-header")
            .end()
        .end()
        .addClass('ui-widget-content');

    contents = $('

')
        .addClass('ui-widget')
        .appendTo(myContainer)
        .append('Nothing Selected');

    ADM.bind("selectionChanged", showProperties);
    ADM.bind("activePageChanged", showProperties);
}
<commit_msg>[Properties] Use a safer method to insert values to input box<commit_after>/*
 * gui-builder - A simple WYSIWYG HTML5 app creator
 * Copyright (c) 2011, Intel Corporation.
 *
 * This program is licensed under the terms and conditions of the
 * Apache License, version 2.0.  The full text of the Apache License is at
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 */
"use strict";

function propertyValueChanged(event) {
    var updated = event.srcElement.id.replace(/-value/,''),
        node = event.data,
        value = null;

    if (node === null || node === undefined) {
        console.error('Missing node data, property change failed!');
        return;
    }

    switch (BWidget.getPropertyType(node.getType(), updated)) {
        case 'boolean':
            value = Boolean($('#' + event.srcElement.id).val());
            break;
        case 'float':
            value = parseFloat($('#' + event.srcElement.id).val());
            break;
        case 'integer':
            value = parseInt($('#' + event.srcElement.id).val(), 10);
            break;
        case 'number':
            value = Number($('#' + event.srcElement.id).val());
            break;
        case 'object':
            value = Object($('#' + event.srcElement.id).val());
            break;
        case 'string':
            value = String($('#' + event.srcElement.id).val());
            break;
        default:
            break;
    }
    console.log(updated + " changed to " + value);
    node.setProperty(updated, value);
    event.node = node;
    showProperties(event);
}

function showProperties(event) {
        var node = event.node,
            showUid = false,
            labelId, labelVal, valueId, valueVal;

        // Clear the properties pane when nothing is selected
        if (node === null || node === undefined) {
            node = ADM.getActivePage();
            if (node === null || node === undefined) {
                $('#property_content').empty()
                    .append('Nothing Selected');
                return;
            }
        }

        console.log("User selected ADMNode_" + node._uid);

        $('#property_content').empty()
            .append('

' +
                    '' +
                    BWidget.getDisplayLabel(node.getType()) +
                    '' +
                    (showUid ? ' (uid=' + node.getUid() + ')' : '') +
                    '

');

        var props = node.getProperties();
        var options = node.getPropertyOptions();
        for (var p in props) {
            if (p == "type_label" || p == "conditional_label" || p == "conditional_for") {
                continue;
            }
            else if (p == "type") {
                labelVal = node.getProperty("type_label");
            }
            else if (p == "conditional") {
                if(node.getProperty("conditional_for") != node.getProperty("type")) {
                    continue;
                }
                labelVal = node.getProperty("conditional_label");
            }
            else {
                labelVal = p.replace(/_/g,'-');
            }
            labelId = p+'-label';
            valueId = p+'-value';
            valueVal = props[p];
            if(options[p]) {
                var code = '
' + labelVal + '	' +
                         '' +
                         '<select id="' + valueId + '">';
                for (var o in options[p]) {
                    if(options[p][o] == props[p]) {
                        code += '<option value="' + options[p][o] + '" selected=true>' + options[p][o] + '</option>';
                    }
                    else {
                        code += '<option value="' + options[p][o] + '">' + options[p][o] + '</option>';
                    }
                }
                code += '</select>' + '
';
                    $('#property_content').children().last()
                        .append(code);
            }
            else {
                $('#property_content').children().last()
                    .append('
' + labelVal + '	' +
                            '' +
                            '<input class="full" id="' + valueId + '" />' +
                            '
');
                $('#'+valueId).val(valueVal);
            }

/*
            $('#select').change( function(event) {
                alert("change");
            });
            $('#select').select( function(event) {
                alert("select");
            });
*/

            $('#' + valueId).change( node, propertyValueChanged );
        }

        $('table','#property_content').attr({ 'cellspacing': '0' });
        $('th:odd, td:odd','#property_content')
            .css({ 'border-top': '1px solid black',
                   'border-bottom': '1px solid black' });
        $('td','#property_content').attr('align','left');
        $('th','#property_content').attr('align','left')
            .addClass('ui-helper-reset ui-state-default');

/*
 * The above should produce something like the following so that it will
 * render as a jQ-UI "tab" object
 *

Properties

Inherited

Custom

Inherited properties go here

Custom properties go here

*/
}

function loadProperties(container) {
    var defaultContainer = '#properties-panel',
        myContainer = container,
        contents;

    if (!myContainer) {
        myContainer = $(defaultContainer);
    }

    if (!myContainer || !myContainer.get()) {
        return false;
    }

    // FIXME: There was a
 here to partially make up for an overlap
    //        issue that appears to be caused by the interaction of jQuery UI
    //        accordion and flex box implementation. The palette wants to
    //        draw over the top of the properties pane, by the height of the
    //        Palette header, which leads me to believe it's calculating its
    //        height from its bottom to the top of its parent. I replaced
    //        this
 with a tweak to the CSS margin of palette-panel.
    myContainer.append('
Properties
')
        .children().last()
            .addClass("ui-helper-reset ui-widget ui-widget-header")
            .end()
        .end()
        .addClass('ui-widget-content');

    contents = $('

')
        .addClass('ui-widget')
        .appendTo(myContainer)
        .append('Nothing Selected');

    ADM.bind("selectionChanged", showProperties);
    ADM.bind("activePageChanged", showProperties);
}
<|endoftext|>


<commit_before>$().ready(function (e) {
    $('.slickslide').slick({
        dots: true,
        infinite: true,
        speed: 500,
        fade: false,
        slide: 'li',
        cssEase: 'linear',
        centerMode: true,
        slidesToShow: 1,
        variableWidth: true,
        autoplay: true,
        autoplaySpeed: 4000,
        responsive: [{
            breakpoint: 800,
            settings: {
                arrows: false,
                centerMode: false,
                centerPadding: '40px',
                variableWidth: false,
                slidesToShow: 1,
                dots: true
            },
            breakpoint: 1024,
            settings: {
                arrows: false,
                centerMode: false,
                centerPadding: '40px',
                variableWidth: false,
                slidesToShow: 1,
                dots: true

            }
        }],
        customPaging: function (slider, i) {
            return '<button class="tab">' + $('.slick-thumbs li:nth-child(' + (i + 1) + ')').html() + '</button>';
        }

    });
    sidebarHeight();

	$( window ).resize(function() {
	  sidebarHeight();
	});
	sidebarHeight = function (){
		$("aside .body").css("height", $(".slick-list").height()-$(".slick-dots").height());
		$("div.price").css("height", $(".slick-dots").height());
	}
	$(".tab-nav li").click(function(){
		$(this).siblings().removeClass("active");
		$(this).addClass("active");
	})
	$(".t1").click(function(){
		$(".tab-content").removeClass("active");
		$(".tc-1").addClass("active");

	})
	$(".t2").click(function(){
		$(".tab-content").removeClass("active");
		$(".tc-2").addClass("active");

	})
	$(".t3").click(function(){
		$(".tab-content").removeClass("active");
		$(".tc-3").addClass("active");

	})
	$(".t4").click(function(){
		$(".tab-content").removeClass("active");
		$(".tc-4").addClass("active");

	})

});<commit_msg>sidebar height fix<commit_after>$().ready(function (e) {

	sidebarHeight = function (){
		$("aside .body").css("height", $(".slick-list").height()-$(".slick-dots").height());
		$("div.price").css("height", $(".slick-dots").height());
	}

    $('.slickslide').slick({
        dots: true,
        infinite: true,
        speed: 500,
        fade: false,
        slide: 'li',
        cssEase: 'linear',
        centerMode: true,
        slidesToShow: 1,
        variableWidth: true,
        autoplay: true,
        autoplaySpeed: 4000,
        responsive: [{
            breakpoint: 800,
            settings: {
                arrows: false,
                centerMode: false,
                centerPadding: '40px',
                variableWidth: false,
                slidesToShow: 1,
                dots: true
            },
            breakpoint: 1024,
            settings: {
                arrows: false,
                centerMode: false,
                centerPadding: '40px',
                variableWidth: false,
                slidesToShow: 1,
                dots: true

            }
        }],
        customPaging: function (slider, i) {
            return '<button class="tab">' + $('.slick-thumbs li:nth-child(' + (i + 1) + ')').html() + '</button>';
        }

    });
    sidebarHeight();

	$( window ).resize(function() {
	  sidebarHeight();
	});

	$(".tab-nav li").click(function(){
		$(this).siblings().removeClass("active");
		$(this).addClass("active");
	})
	$(".t1").click(function(){
		$(".tab-content").removeClass("active");
		$(".tc-1").addClass("active");

	})
	$(".t2").click(function(){
		$(".tab-content").removeClass("active");
		$(".tc-2").addClass("active");

	})
	$(".t3").click(function(){
		$(".tab-content").removeClass("active");
		$(".tc-3").addClass("active");

	})
	$(".t4").click(function(){
		$(".tab-content").removeClass("active");
		$(".tc-4").addClass("active");

	})

});<|endoftext|>


<commit_before>var $ = require('wetfish-basic');
var storage = require('../app/storage');

var animate =
{
    active: false,
    element: false,
    object: false,
    frame: 0,
    frames: [],

    init: function()
    {
        // Save the default element notification text
        animate.defaultText = $('.menu .animate .element').el[0].textContent;

        $('body').on('click', '.workspace .content, .workspace .content *', function()
        {
            var element = this;

            // If we clicked on a child element
            if(!$(element).hasClass('content'))
            {
                // Find the parent content element
                element = $(this).parents('.content').el[0];
            }

            if(animate.active)
            {
                animate.element = element;
                animate.populate();
                animate.menu();
            }
        });

        $('.animate .new').on('click', function()
        {
             var name = prompt("Please enter a memorable name for this animation.");

             if(animate.object.animation !== undefined && animate.object.animation[name] !== undefined)
             {
                var replace = confirm("An animation named '" + name + "' already exists. Are you sure you want to overwrite it?");

                if(!replace)
                {
                    return;
                }
             }

            animate.name = name;
            animate.frame = 0;
            animate.frames = [];

            storage.animation.save(animate.element, animate.name, animate.frames);
            animate.populate();
        });

        $('.animate .animations select').on('change', function(event)
        {
            var animation = $(this).value();

            // Only trigger when an actual animation is selected
            if(animation)
            {
                $('.animation-selected').removeClass('hidden');
                animate.name = animation;
                animate.frame = 0;
                animate.frames = animate.object[animate.name];
            }
            else
            {
                $('.animation-selected').addClass('hidden');
            }
        });

        $('.animate .delete').on('click', function()
        {
            storage.animation.delete(animate.element, animate.name);
            animate.populate();
        });

        $('.workspace').on('content-updated', function(event)
        {
            var updatedElement = event.detail;

            if(animate.element == updatedElement)
            {
                animate.save();
            }
        });
    },

    // Populate data from saved object
    populate: function()
    {
        var id = $(animate.element).attr('id');
        var object = storage.getObject(id);

        animate.object = object;

        var  desc = object.desc || 'untitled ' + object.type;
        $('.menu .animate .element').text(desc);

        if(object.animation !== undefined && Object.keys(object.animation).length)
        {
            // Remove any previously created animations
            $('.animate .animations .custom').remove();
            $('.animate .animations').removeClass('hidden');

            // Loop through saved animatinos and populate the dropdown
            Object.keys(object.animation).forEach(function(name)
            {
                var option = document.createElement('option');
                $(option).addClass('custom');
                $(option).text(name);
                $('.animate .animations select').append(option).trigger('change');
            });
        }
        else
        {
            $('.animate .animations').addClass('hidden');
        }
    },

    // Save current transform data
    save: function()
    {
        // Get the current computed transform style
        var transform = $(animate.element).style('transform');
        animate.frames[animate.frame] = transform;

        storage.animation.save(animate.element, animate.name, animate.frames);
    },

    // Display menu options based on the current animation state
    menu: function()
    {
        if(animate.element)
        {
            $('.element-selected').removeClass('hidden');
        }
        else
        {
            $('.element-selected').addClass('hidden');
        }
    },

    start: function()
    {
        $('.workspace').addClass('highlight-content');
        animate.active = true;
    },

    stop: function()
    {
        $('.workspace').removeClass('highlight-content');
        animate.active = false;
        animate.element = false;
        animate.object = false;

        $('.menu .animate .element').text(animate.defaultText);
    }
};

module.exports = animate;
<commit_msg>Enable hover menus when animating and an element is selected<commit_after>var $ = require('wetfish-basic');
var storage = require('../app/storage');
var helper = require('../app/helper');

var animate =
{
    active: false,
    element: false,
    object: false,
    frame: 0,
    frames: [],

    init: function()
    {
        // Save the default element notification text
        animate.defaultText = $('.menu .animate .element').el[0].textContent;

        $('body').on('click', '.workspace .content, .workspace .content *', function()
        {
            var element = this;

            // If we clicked on a child element
            if(!$(element).hasClass('content'))
            {
                // Find the parent content element
                element = $(this).parents('.content').el[0];
            }

            if(animate.active)
            {
                animate.element = element;
                animate.populate();
                animate.menu();
            }
        });

        $('.animate .new').on('click', function()
        {
             var name = prompt("Please enter a memorable name for this animation.");

             if(animate.object.animation !== undefined && animate.object.animation[name] !== undefined)
             {
                var replace = confirm("An animation named '" + name + "' already exists. Are you sure you want to overwrite it?");

                if(!replace)
                {
                    return;
                }
             }

            animate.name = name;
            animate.frame = 0;
            animate.frames = [];

            storage.animation.save(animate.element, animate.name, animate.frames);
            animate.populate();
        });

        $('.animate .animations select').on('change', function(event)
        {
            var animation = $(this).value();

            // Only trigger when an actual animation is selected
            if(animation)
            {
                $('.animation-selected').removeClass('hidden');
                animate.name = animation;
                animate.frame = 0;
                animate.frames = animate.object.animation[animate.name];

                helper.hover.enabled = true;
            }
            else
            {
                $('.animation-selected').addClass('hidden');
                helper.hover.enabled = false;
            }
        });

        $('.animate .delete').on('click', function()
        {
            storage.animation.delete(animate.element, animate.name);
            animate.populate();
        });

        $('.workspace').on('content-updated', function(event)
        {
            var updatedElement = event.detail;

            if(animate.element == updatedElement)
            {
                animate.save();
            }
        });
    },

    // Populate data from saved object
    populate: function()
    {
        var id = $(animate.element).attr('id');
        var object = storage.getObject(id);

        animate.object = object;

        var  desc = object.desc || 'untitled ' + object.type;
        $('.menu .animate .element').text(desc);

        if(object.animation !== undefined && Object.keys(object.animation).length)
        {
            // Remove any previously created animations
            $('.animate .animations .custom').remove();
            $('.animate .animations').removeClass('hidden');

            // Loop through saved animatinos and populate the dropdown
            Object.keys(object.animation).forEach(function(name)
            {
                var option = document.createElement('option');
                $(option).addClass('custom');
                $(option).text(name);
                $('.animate .animations select').append(option).trigger('change');
            });
        }
        else
        {
            $('.animate .animations').addClass('hidden');
        }
    },

    // Save current transform data
    save: function()
    {
        // Get the current computed transform style
        var transform = $(animate.element).style('transform');
        animate.frames[animate.frame] = transform;
        animate.object.animation[animate.name] = animate.frames;

        storage.animation.save(animate.element, animate.name, animate.frames);
    },

    // Display menu options based on the current animation state
    menu: function()
    {
        if(animate.element)
        {
            $('.element-selected').removeClass('hidden');
        }
        else
        {
            $('.element-selected').addClass('hidden');
        }
    },

    start: function()
    {
        $('.workspace').addClass('highlight-content');
        animate.active = true;
    },

    stop: function()
    {
        $('.workspace').removeClass('highlight-content');
        animate.active = false;
        animate.element = false;
        animate.object = false;

        $('.menu .animate .element').text(animate.defaultText);
    }
};

module.exports = animate;
<|endoftext|>


<commit_before>var $ = require('wetfish-basic');
var storage = require('../app/storage');
var helper = require('../app/helper');

var animate =
{
    active: false,
    element: false,
    object: false,
    frame: 0,
    frames: [],
    animation: false,
    duration: 1000,
    iterations: Infinity,
    playing: false,

    init: function()
    {
        // Save the default element notification text
        animate.defaultText = $('.menu .animate .element').el[0].textContent;

        $('body').on('click', '.workspace .content, .workspace .content *', function()
        {
            var element = this;

            // If we clicked on a child element
            if(!$(element).hasClass('content'))
            {
                // Find the parent content element
                element = $(this).parents('.content').el[0];
            }

            if(animate.active)
            {
                animate.element = element;
                animate.populate();
                animate.menu();
            }
        });

        $('.animate .new').on('click', function()
        {
             var name = prompt("Please enter a memorable name for this animation.");

             if(animate.object.animation !== undefined && animate.object.animation[name] !== undefined)
             {
                var replace = confirm("An animation named '" + name + "' already exists. Are you sure you want to overwrite it?");

                if(!replace)
                {
                    return;
                }
             }

            animate.name = name;
            animate.frame = 0;
            animate.frames = [];

            animate.populate();
            animate.persist();
        });

        $('.animate .animations select').on('change', function(event)
        {
            var animation = $(this).value();

            // Only trigger when an actual animation is selected
            if(animation)
            {
                animate.name = animation;
                animate.frame = 0;
                animate.frames = animate.object.animation[animate.name].frames;

                $('.animation-selected').removeClass('hidden');

                $('#frame').value(0);
                $('#frame').attr('max', animate.frames.length - 1);

                if(animate.object.animation[animate.name].iterations == 'Infinity')
                {
                    $('.animate .loop').prop('checked', true);
                }
                else
                {
                    $('.animate .loop').prop('checked', false);
                }

                $('#duration').value(animate.object.animation[animate.name].duration).trigger('change');

                if(animate.object.animations.indexOf(animation) > -1)
                {
                    $('.animate .enabled').prop('checked', true).trigger('change');
                }
                else
                {
                    $('.animate .enabled').prop('checked', false).trigger('change');
                }

                helper.hover.enabled = true;

                animate.update();
            }
            else
            {
                $('.animation-selected').addClass('hidden');
                helper.hover.enabled = false;
            }
        });

        $('.animate .new-frame').on('click', function()
        {
            // Add a new frame after the current position
            animate.frames.splice(animate.frame + 1, 0, {});
            animate.frame++;

            $('#frame').attr('max', animate.frames.length - 1);
            $('#frame').value(animate.frame);

            animate.persist();
        });

        $('#duration').on('input change', function()
        {
            animate.duration = parseInt($(this).value());
            animate.persist();
            animate.refresh();
        });

        $('#frame').on('input change', function()
        {
            // If the frame has changed and there is more than one frame
            if(animate.frame != $(this).value())
            {

                // Set the current time of the animation to match the value of the slider
                animate.frame = parseInt($(this).value());
                animate.update();
            }
        });

        // Delete an entire animation
        $('.animate .delete').on('click', function()
        {
            storage.animation.delete(animate.element, animate.name);
            animate.populate();
        });

        $('.animate .enabled').on('click change', function()
        {
            if(this.checked)
            {
                animate.play();
            }
            else
            {
                animate.pause();
            }

            animate.persist();
        });

        $('.animate .loop').on('click change', function()
        {
            if(this.checked)
            {
                animate.iterations = Infinity;
            }
            else
            {
                animate.iterations = 1;
            }

            animate.persist();
            animate.refresh();
        });

        $('.workspace').on('content-updated', function(event)
        {
            var updatedElement = event.detail;

            if(animate.element == updatedElement)
            {
                animate.persist();
            }
        });
    },

    // Populate data from saved object
    populate: function()
    {
        var id = $(animate.element).attr('id');
        var object = storage.getObject(id);

        animate.object = object;

        var  desc = object.desc || 'untitled ' + object.type;
        $('.menu .animate .element').text(desc);

        if(object.animation !== undefined && Object.keys(object.animation).length)
        {
            // Remove any previously created animations
            $('.animate .animations .custom').remove();
            $('.animate .animations').removeClass('hidden');

            // Loop through saved animatinos and populate the dropdown
            Object.keys(object.animation).forEach(function(name)
            {
                var option = document.createElement('option');
                $(option).addClass('custom');
                $(option).text(name);
                $('.animate .animations select').append(option).trigger('change');
            });
        }
        else
        {
            $('.animate .animations').addClass('hidden');
        }
    },

    // Save current transform data
    persist: function()
    {
        // If the animation is not currently playing
        if(!animate.animation || (animate.animation && animate.animation.playState != 'running'))
        {
            // Get the current computed transform style
            var transform = $(animate.element).style('transform');

            // Update the saved animation object
            animate.frames[animate.frame] = JSON.parse(JSON.stringify({'computed': transform, 'basic': animate.element.transform}));
            animate.object.animation[animate.name] = animate.frames;
        }

        // Build object of data to be saved
        var data =
        {
            duration: animate.duration,
            iterations: animate.iterations,
            frames: animate.frames,
            playing: animate.playing
        };

        if(animate.iterations === Infinity)
        {
            data.iterations = 'Infinity';
        }

        storage.animation.save(animate.element, animate.name, data);
    },

    // Update animation element with the values of the current frame
    update: function()
    {
        var frame = animate.frames[animate.frame];
        $(animate.element).transform(frame.basic);
    },

    play: function()
    {
        try
        {
            var frames = [];

            animate.frames.forEach(function(frame)
            {
                frames.push(frame.computed);
            });

            if(animate.animation)
            {
                animate.animation.cancel();
            }

            animate.animation = animate.element.animate({'transform': frames}, {'duration': animate.duration, 'iterations': animate.iterations, 'fill': 'forwards'});

            var durationPerFrame = animate.duration / (animate.frames.length - 1);
            var currentTime = durationPerFrame * animate.frame;

            if(currentTime > 1)
            {
                currentTime--;
            }

            animate.animation.currentTime = currentTime;
            helper.hover.enabled = false;
            animate.playing = true;
        }
        catch(exception)
        {
            console.log(exception);

            animate.animation = false;
            helper.hover.enabled = true;
            animate.playing = false;
        }
    },

    // Refresh animation if currently playing
    refresh: function()
    {
        if(animate.animation && animate.animation.playState == 'running')
        {
            animate.play();
        }
    },

    pause: function()
    {
        if(animate.animation)
        {
            animate.animation.cancel();
            helper.hover.enabled = true;
            animate.playing = false;
        }
    },

    // Display menu options based on the current animation state
    menu: function()
    {
        if(animate.element)
        {
            $('.element-selected').removeClass('hidden');
        }
        else
        {
            $('.element-selected').addClass('hidden');
        }
    },

    start: function()
    {
        $('.workspace').addClass('highlight-content');
        animate.active = true;
        helper.storage.update = false;
    },

    stop: function()
    {
        $('.workspace').removeClass('highlight-content');
        animate.pause();
        animate.active = false;
        animate.element = false;
        animate.object = false;
        helper.storage.update = true;

        $('.menu .animate .element').text(animate.defaultText);
    }
};

module.exports = animate;
<commit_msg>Hide animation options after canceling<commit_after>var $ = require('wetfish-basic');
var storage = require('../app/storage');
var helper = require('../app/helper');

var animate =
{
    active: false,
    element: false,
    object: false,
    frame: 0,
    frames: [],
    animation: false,
    duration: 1000,
    iterations: Infinity,
    playing: false,

    init: function()
    {
        // Save the default element notification text
        animate.defaultText = $('.menu .animate .element').el[0].textContent;

        $('body').on('click', '.workspace .content, .workspace .content *', function()
        {
            var element = this;

            // If we clicked on a child element
            if(!$(element).hasClass('content'))
            {
                // Find the parent content element
                element = $(this).parents('.content').el[0];
            }

            if(animate.active)
            {
                animate.element = element;
                animate.populate();
                animate.menu();
            }
        });

        $('.animate .new').on('click', function()
        {
             var name = prompt("Please enter a memorable name for this animation.");

             if(animate.object.animation !== undefined && animate.object.animation[name] !== undefined)
             {
                var replace = confirm("An animation named '" + name + "' already exists. Are you sure you want to overwrite it?");

                if(!replace)
                {
                    return;
                }
             }

            animate.name = name;
            animate.frame = 0;
            animate.frames = [];

            animate.populate();
            animate.persist();
        });

        $('.animate .animations select').on('change', function(event)
        {
            var animation = $(this).value();

            // Only trigger when an actual animation is selected
            if(animation)
            {
                animate.name = animation;
                animate.frame = 0;
                animate.frames = animate.object.animation[animate.name].frames;

                $('.animation-selected').removeClass('hidden');

                $('#frame').value(0);
                $('#frame').attr('max', animate.frames.length - 1);

                if(animate.object.animation[animate.name].iterations == 'Infinity')
                {
                    $('.animate .loop').prop('checked', true);
                }
                else
                {
                    $('.animate .loop').prop('checked', false);
                }

                $('#duration').value(animate.object.animation[animate.name].duration).trigger('change');

                if(animate.object.animations.indexOf(animation) > -1)
                {
                    $('.animate .enabled').prop('checked', true).trigger('change');
                }
                else
                {
                    $('.animate .enabled').prop('checked', false).trigger('change');
                }

                animate.update();
            }
            else
            {
                $('.animation-selected').addClass('hidden');
                helper.hover.enabled = false;
            }
        });

        $('.animate .new-frame').on('click', function()
        {
            // Add a new frame after the current position
            animate.frames.splice(animate.frame + 1, 0, {});
            animate.frame++;

            $('#frame').attr('max', animate.frames.length - 1);
            $('#frame').value(animate.frame);

            animate.persist();
        });

        $('#duration').on('input change', function()
        {
            animate.duration = parseInt($(this).value());
            animate.persist();
            animate.refresh();
        });

        $('#frame').on('input change', function()
        {
            // If the frame has changed and there is more than one frame
            if(animate.frame != $(this).value())
            {

                // Set the current time of the animation to match the value of the slider
                animate.frame = parseInt($(this).value());
                animate.update();
            }
        });

        // Delete an entire animation
        $('.animate .delete').on('click', function()
        {
            storage.animation.delete(animate.element, animate.name);
            animate.populate();
        });

        $('.animate .enabled').on('click change', function()
        {
            if(this.checked)
            {
                animate.play();
            }
            else
            {
                animate.pause();
            }

            animate.persist();
        });

        $('.animate .loop').on('click change', function()
        {
            if(this.checked)
            {
                animate.iterations = Infinity;
            }
            else
            {
                animate.iterations = 1;
            }

            animate.persist();
            animate.refresh();
        });

        $('.workspace').on('content-updated', function(event)
        {
            var updatedElement = event.detail;

            if(animate.element == updatedElement)
            {
                animate.persist();
            }
        });
    },

    // Populate data from saved object
    populate: function()
    {
        var id = $(animate.element).attr('id');
        var object = storage.getObject(id);

        animate.object = object;

        var  desc = object.desc || 'untitled ' + object.type;
        $('.menu .animate .element').text(desc);

        if(object.animation !== undefined && Object.keys(object.animation).length)
        {
            // Remove any previously created animations
            $('.animate .animations .custom').remove();
            $('.animate .animations').removeClass('hidden');
            $('.animate select option').eq(0).prop('selected', true);

            // Loop through saved animatinos and populate the dropdown
            Object.keys(object.animation).forEach(function(name)
            {
                var option = document.createElement('option');
                $(option).addClass('custom');
                $(option).text(name);
                $('.animate .animations select').append(option).trigger('change');
            });
        }
        else
        {
            $('.animate .animations').addClass('hidden');
        }
    },

    // Save current transform data
    persist: function()
    {
        // If the animation is not currently playing
        if(!animate.animation || (animate.animation && animate.animation.playState != 'running'))
        {
            // Get the current computed transform style
            var transform = $(animate.element).style('transform');

            // Update the saved animation object
            animate.frames[animate.frame] = JSON.parse(JSON.stringify({'computed': transform, 'basic': animate.element.transform}));
            animate.object.animation[animate.name] = animate.frames;
        }

        // Build object of data to be saved
        var data =
        {
            duration: animate.duration,
            iterations: animate.iterations,
            frames: animate.frames,
            playing: animate.playing
        };

        if(animate.iterations === Infinity)
        {
            data.iterations = 'Infinity';
        }

        storage.animation.save(animate.element, animate.name, data);
    },

    // Update animation element with the values of the current frame
    update: function()
    {
        var frame = animate.frames[animate.frame];
        $(animate.element).transform(frame.basic);
    },

    play: function()
    {
        try
        {
            var frames = [];

            animate.frames.forEach(function(frame)
            {
                frames.push(frame.computed);
            });

            if(animate.animation)
            {
                animate.animation.cancel();
            }

            animate.animation = animate.element.animate({'transform': frames}, {'duration': animate.duration, 'iterations': animate.iterations, 'fill': 'forwards'});

            var durationPerFrame = animate.duration / (animate.frames.length - 1);
            var currentTime = durationPerFrame * animate.frame;

            if(currentTime > 1)
            {
                currentTime--;
            }

            animate.animation.currentTime = currentTime;
            helper.hover.enabled = false;
            animate.playing = true;
        }
        catch(exception)
        {
            console.log(exception);

            animate.animation = false;
            helper.hover.enabled = true;
            animate.playing = false;
        }
    },

    // Refresh animation if currently playing
    refresh: function()
    {
        if(animate.animation && animate.animation.playState == 'running')
        {
            animate.play();
        }
    },

    pause: function()
    {
        if(animate.animation)
        {
            animate.animation.cancel();
            helper.hover.enabled = true;
            animate.playing = false;
        }
    },

    // Display menu options based on the current animation state
    menu: function()
    {
        if(animate.element)
        {
            $('.element-selected').removeClass('hidden');
        }
        else
        {
            $('.element-selected').addClass('hidden');
        }
    },

    start: function()
    {
        $('.workspace').addClass('highlight-content');
        animate.active = true;
        helper.storage.update = false;
    },

    stop: function()
    {
        $('.workspace').removeClass('highlight-content');
        $('.element-selected').addClass('hidden');
        $('.animation-selected').addClass('hidden');

        animate.pause();
        animate.active = false;
        animate.element = false;
        animate.object = false;
        helper.storage.update = true;

        $('.menu .animate .element').text(animate.defaultText);
    }
};

module.exports = animate;
<|endoftext|>