<commit_before>(function($, undefined) {

  $.fn.spritespin = function(method) {
    if ( methods[method] ) {
      return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
    } else if (typeof(method) === 'object' || !method) {
      return methods.init.apply(this, arguments);
    } else {
      $.error( 'Method ' +  method + ' does not exist on jQuery.spritespin' );
    }
  };

  var methods = {
    init : function(options){
      // Default settings
      var settings = {
        // dimensions
        offsetX           : 0,                      // X offset where the frame image starts
        offsetY           : 0,                      // Y offset where the frame image starts
        width             : undefined,              // Window width (or frame width)
        height            : undefined,              // Window height (or frame height)
        frameStep         : undefined,              // Width of a single frame or step to the next frame
        frames            : 36,                     // Number of frames
        frame             : 0,                      // Initial frame number

        // animation & update
        animate           : true,                   // Run animation when after initialize
        loop              : false,                  // Repeat animation in a loop
        loopFrame         : 0,
        frameTime         : 36,                     // Time between updates
        reverse           : false,                  // If true animation is played backward
        sense             : 1,

        // interaction
        slider            : undefined,              // jQuery-ui slider instance
        behavior          : "drag",                 // Enables mouse interaction

        // appearance
        image             : "images/spritespin.jpg",// Stiched source image
        preloadText       : "Loading",              // Text to appear when images are preloaded
        preloadBackground : undefined,

        // events
        onFrame           : undefined,
        onLoad            : undefined
      }

      //extending options
      options = (options || {});
      $.extend(settings, options);

      return this.each(function(){
        var $this = $(this);
        var data  = $this.data('spritespin');

        if (!data){
          // disable selection
          $this.attr("unselectable", "on");

          // Initialize the plugin if it hasn't been initialized yet
          $this.data('spritespin', {
            target    : $this,
            settings  : settings,
            animation : null,
            touchable : (/iphone|ipod|ipad|android/i).test(navigator.userAgent),
            frameTime : settings.frameTime,
          });

          // run configuration
          data = $this.data('spritespin');
          helper.reconfiger($this, data);
        } else {
          // reconfiger the plugin if it is already initialized
          $.extend(data.settings, options);
          helper.reconfiger($this, data);
        }
      });
    },
    destroy : function(){
      return this.each(function(){
        var $this = $(this);
        data = $this.data('spritespin');
        $this.unbind('.spritespin');
        $this.removeData('spritespin');
      });
    },
    // Updates a single frame to the specified frame number. If no value is
    // given this will increment the current frame counter.
    // Triggers the onFrame event
    update : function(frame, reverse){
      return this.each(function(){
        var $this = $(this);
        data = $this.data('spritespin');
        settings = data.settings;

        if (reverse != undefined){
          settings.reverse = reverse;
        }

        // update frame counter
        if (frame == undefined){
          settings.frame = (settings.frame + (settings.reverse ? -1 : 1));
        } else {
          settings.frame = frame;
        }
        settings.frame = helper.wrapValue(settings.frame, 0, settings.frames);
        data.target.trigger("onFrame", data);
      });
    },
    // Starts or stops the animation depend on the animate paramter.
    // In case when animation is already running pass "false" to stop.
    // In case when animation is not running pass "true" to start.
    // To keep animation running forever pass "true" for the loop parameter.
    // To detect whether the animation is running or not, do not pass any
    // parameters.
    animate : function(animate, loop){
      if (animate == undefined){
        return $(this).data('spritespin').animation != null;
      } else {
        return this.each(function(){
          var $this = $(this);
          data = $this.data('spritespin');
          settings = data.settings;

          // check the loop variable and update settings
          if (typeof(loop) == "boolean"){
            settings.loop = loop;
          }

          // toggle and update animation settings
          if (animate == "toggle"){
            animate = !settings.animate
            settings.animate = animate;
          } else {
            settings.animate = animate;
          }

          if (animate && data.animation == null){
            // start animation
            data.animation = setInterval(
              function(){
                try {
                  methods.update.apply($this, []);
                } catch(err){
                  // The try catch block is a hack for Opera Browser
                }
              }, data.frameTime);
          } else if (!animate && data.animation != null) {
            // stop animation
            clearInterval(data.animation);
            data.animation = null;
          }
        });
      }
    },
    // Gets the current framenumber when no parameters are passed or
    // updates the player to the sepcified framenumber.
    frame : function(frame){
      if (frame == undefined){
        return $(this).data('spritespin').settings.frame;
      } else {
        return this.each(function(){
          methods.update.apply($(this), [frame]);
        });
      }
    },
    // Gets a value indication whether the animation is looped or not when
    // no parameters are passed. Otherwise sets the loop setting and starts
    // the animation when settings.animate is set to true
    loop : function(value){
      if (value == undefined){
        return $(this).data('spritespin').settings.loop;
      } else {
        return this.each(function(){
          var $this = $(this);
          data = $this.data('spritespin');
          methods.animate.apply($(this), [data.settings.animate, value]);
        });
      }
    },
  };

  var helper = {
    reconfiger : function(instance, data){
      helper.blankBackground(instance, data);
      helper.preloadImages(instance, data, function(){
        helper.updateBackground(instance, data);
        helper.hookSlider(instance, data);
        helper.rebindEvents(instance, data);
        if (data.settings.animate){
          methods.animate.apply(instance, [data.settings.animate, data.settings.loop]);
        }
        instance.trigger("onLoad", data);
      });
    },
    wrapValue : function(value, min, max){
      while (value >= max){
        value -= max;
      }
      while (value < min){
        value += max;
      }
      return value;
    },
    blankBackground : function(instance, data){
      image = "none";
      if (data.settings.preloadBackground != undefined){
        image = ["url('", data.settings.preloadBackground, "')"].join("");
      }
      instance.css({
        width      : [data.settings.width, "px"].join(""),
        height     : [data.settings.height, "px"].join(""),
        "background-image"    : image,
        "background-repeat"   : "repeat-x",
        "background-position" : "0px 0px"
      });
    },
    updateBackground : function(instance){
      var data = instance.data("spritespin");
      var image = data.settings.image;
      var x = data.settings.offsetX;
      var y = -data.settings.offsetY;

      if (typeof(data.settings.image) == "string"){
        if (data.settings.frameStep != undefined){
          x -= (data.settings.frame * data.settings.frameStep);
        } else {
          x -= (data.settings.frame * data.settings.width);
        }
      } else {
        image = data.settings.image[data.settings.frame];
      }

      instance.css({
        width      : [data.settings.width, "px"].join(""),
        height     : [data.settings.height, "px"].join(""),
        "background-image"    : ["url('", image, "')"].join(""),
        "background-repeat"   : "repeat-x",
        "background-position" : [x, "px ", y, "px"].join("")
      });
    },
    hookSlider : function(instance, data){
      if (data.settings.slider != undefined){
        data.settings.slider.slider({
          value   : data.settings.frame,
          min     : 0,
          max     : (data.settings.frames) - 1,
          step    : 1,
          slide   : function(event, ui) {
            methods.animate.apply(instance, [false]);    // stop animation
            methods.frame.apply(instance, [ui.value]);  // update to frame
          },
        });
      }
    },
    rebindEvents : function(instance, data){
      // unbind all events
      instance.unbind('.spritespin');

      // rebind interaction events
      if (data.touchable){
        instance.bind('touchstart.spritespin', behavior[data.settings.behavior].mousedown);
        instance.bind('touchmove.spritespin',  behavior[data.settings.behavior].mousemove);
        instance.bind('touchend.spritespin',   behavior[data.settings.behavior].mouseup);
      }
      instance.bind('mousedown.spritespin',  behavior[data.settings.behavior].mousedown);
      instance.bind('mousemove.spritespin',  behavior[data.settings.behavior].mousemove);
      instance.bind('mouseup.spritespin',    behavior[data.settings.behavior].mouseup);
      instance.bind('mouseenter.spritespin',  behavior[data.settings.behavior].mouseenter);
      instance.bind('mouseover.spritespin',  behavior[data.settings.behavior].mouseover);
      instance.bind('mouseleave.spritespin', behavior[data.settings.behavior].mouseleave);
      instance.bind('onFrame.spritespin', behavior[data.settings.behavior].onFrame);

      // disable selection
	    instance.bind("mousedown.spritespin selectstart.spritespin",
	    		function( event ) { event.preventDefault(); }
	    );

	    instance.bind("onFrame.spritespin", function(event, data){
	      helper.updateBackground(data.target, data);

        // stop animation if we are back at frame 0
        if (data.settings.frame == data.settings.loopFrame && !data.settings.loop){
          methods.animate.apply(data.target, [false]);
        }

        // update the jquery-ui slider
        if (data.settings.slider != undefined){
          data.settings.slider.slider("value", data.settings.frame);
        }
	    });

	    // bind custom events
	    if (data.settings.onFrame != undefined){
	      instance.bind("onFrame.spritespin", data.settings.onFrame);
	    }

	    if (data.settings.onLoad != undefined){
	      instance.bind("onLoad.spritespin", data.settings.onLoad);
	    }
    },
    preloadImages : function(instance, data, callback) {
      var preload = $('<div class="preload"/>');
      if (instance.find(".preload").length == 0){
        instance.append(preload);
      }
      preload.html("Loading");

      var i = 0;
      var total = 1;
      var images = [data.settings.image];

      if (typeof(data.settings.image) == "object"){
        total = data.settings.image.length;
        images = data.settings.image;
      }

      imageObj = new Image();

      for(i = 0; i < total; i++){
        if (i == (total - 1)){
          imageObj.onload = function(){
            instance.find(".preload").detach();
            callback.apply([instance, data]);
          }
        }
        imageObj.src=images[i];
      }
    },
  };

  var behavior = {
    helper : {
      storePoints : function(e, data){
        data.oldX = data.currentX;
        data.oldY = data.currentY;
        data.currentX = (data.touchable ? e.touches[0].clientX : e.clientX);
        data.currentY = (data.touchable ? e.touches[0].clientY : e.clientY);

        if (data.startX == undefined || data.startY == undefined){
          data.startX = data.currentX;
          data.startY = data.currentY;
          data.clickframe = data.settings.frame;
        }

        if (data.oldX == undefined || data.oldY == undefined){
          data.oldX = data.currentX;
          data.oldY = data.currentY;
        }

        data.dX = data.currentX - data.startX;
        data.dY = data.currentY - data.startY;

        data.ddX = data.currentX - data.oldX;
        data.ddY = data.currentY - data.oldY;
        return false;
      },
      resetPoints : function(e, data){
        data.startX = undefined;
        data.startY = undefined;
        data.currentX = undefined;
        data.currentY = undefined;
        data.oldX = undefined;
        data.oldY = undefined;
        data.dX = 0;
        data.dY = 0;
        data.ddX = 0;
        data.ddY = 0;
      },
      clamp : function(value, min, max){
        return (value > max ? max : (value < min ? min : value));
      }
    },

    none : {
      mousedown  : function(e){ return false; },
      mousemove  : function(e){ return false; },
      mouseup    : function(e){ return false; },

      mouseenter : function(e){ return false; },
      mouseover  : function(e){ return false; },
      mouseleave : function(e){ return false; },

      onFrame : function(e, frame){ return false; }
    },
    spin : {
      mousedown  : function(e){
        var $this = $(this), data = $this.data('spritespin');
        behavior.helper.storePoints(e, data);
        data.onDrag = true;
        return false;
      },
      mousemove  : function(e){
        var $this = $(this), data = $this.data('spritespin');
        if (data.onDrag){
          // perform default drag behavior
          behavior.helper.storePoints(e, data);
          d = data.dX / data.settings.width;
          dFrame = d * data.settings.frames * data.settings.sense;
          frame = Math.round(data.clickframe + dFrame);

          methods.update.apply($this, [frame]);     // update to frame
          methods.animate.apply($this, [false]);  // stop animation

          // calculate framtetime for spinwheel
          if (data.ddX != 0){
            d = data.ddX / data.settings.width;
            dFrame = d * data.settings.frames * data.settings.sense;
            data.frameTime = (data.settings.frameTime / dFrame);
            data.settings.reverse = (data.ddX < 0);
          }
        }
        return false;
      },
      mouseup    : function(e){
        var $this = $(this), data = $this.data('spritespin');
        if (data.onDrag){
          data.onDrag = false;
          $(this).spritespin("animate", true);
        }
        return false;
      },

      mouseenter : function(e){ return false; },
      mouseover  : function(e){ return false; },
      mouseleave : function(e){
        var $this = $(this), data = $this.data('spritespin');
        if (data.onDrag){
          data.onDrag = false;
          $(this).spritespin("animate", true);
        }
        return false;
      },
      onFrame : function(e, data){
        if (data.ddX != 0){
          data.frameTime = data.frameTime + 1;

          $(this).spritespin("animate", false);
          if (data.frameTime < 62){
            $(this).spritespin("animate", true);
          }
        } else {
          $(this).spritespin("animate", false);
        }
        return false;
      }
    },
    drag : {
      mousedown  : function(e){
        var $this = $(this), data = $this.data('spritespin');
        behavior.helper.storePoints(e, data);
        data.onDrag = true;
        data.cachedAnimate = $this.spritespin("animate");
        return false;
      },
      mousemove  : function(e){
        var $this = $(this), data = $this.data('spritespin');
        if (data.onDrag){
          behavior.helper.storePoints(e, data);
          d = data.dX / data.settings.width;
          dFrame = d * data.settings.frames * data.settings.sense;
          frame = Math.round(data.clickframe + dFrame);

          methods.update.apply($this, [frame]);     // update to frame
          methods.animate.apply($(this), [false]);  // stop animation
        }
        return false;
      },
      mouseup    : function(e){
        var $this = $(this), data = $this.data('spritespin');
        behavior.helper.resetPoints(e, data);
        data.onDrag = false;
        methods.animate.apply($(this), [data.cachedAnimate]);
        return false;
      },

      mouseenter : function(e){ return false; },
      mouseover  : function(e){ return false; },
      mouseleave : function(e){
        var $this = $(this), data = $this.data('spritespin');
        behavior.helper.resetPoints(e, data);
        data.onDrag = false;
        methods.animate.apply($(this), [data.cachedAnimate]);
        return false;
      },

      onFrame : function(e, frame){ return false; }
    },
  };
})(jQuery);
<commit_msg>touch handling<commit_after>(function($, undefined) {

  $.fn.spritespin = function(method) {
    if ( methods[method] ) {
      return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
    } else if (typeof(method) === 'object' || !method) {
      return methods.init.apply(this, arguments);
    } else {
      $.error( 'Method ' +  method + ' does not exist on jQuery.spritespin' );
    }
  };

  var methods = {
    init : function(options){
      // Default settings
      var settings = {
        // dimensions
        offsetX           : 0,                      // X offset where the frame image starts
        offsetY           : 0,                      // Y offset where the frame image starts
        width             : undefined,              // Window width (or frame width)
        height            : undefined,              // Window height (or frame height)
        frameStep         : undefined,              // Width of a single frame or step to the next frame
        frames            : 36,                     // Number of frames
        frame             : 0,                      // Initial frame number

        // animation & update
        animate           : true,                   // Run animation when after initialize
        loop              : false,                  // Repeat animation in a loop
        loopFrame         : 0,                      // Indicates the loop start frame
        frameTime         : 36,                     // Time between updates
        reverse           : false,                  // If true animation is played backward
        sense             : 1,                      // Interaction sensitivity used by behavior implementations

        // interaction
        slider            : undefined,              // jQuery-ui slider instance
        behavior          : "drag",                 // Enables mouse interaction

        // appearance
        image             : "images/spritespin.jpg",// Stiched source image
        preloadText       : "Loading",              // Text to appear when images are preloaded
        preloadBackground : undefined,              // Background image to display on load

        // events
        onFrame           : undefined,              // Occurs whe frame has been updated
        onLoad            : undefined               // Occurs when images are loaded
      }

      //extending options
      options = (options || {});
      $.extend(settings, options);

      return this.each(function(){
        var $this = $(this);
        var data  = $this.data('spritespin');

        if (!data){
          // disable selection
          $this.attr("unselectable", "on");

          // Initialize the plugin if it hasn't been initialized yet
          $this.data('spritespin', {
            target    : $this,
            settings  : settings,
            animation : null,
            frameTime : settings.frameTime,
          });

          // run configuration
          data = $this.data('spritespin');
          helper.reconfiger($this, data);
        } else {
          // reconfiger the plugin if it is already initialized
          $.extend(data.settings, options);
          helper.reconfiger($this, data);
        }
      });
    },
    destroy : function(){
      return this.each(function(){
        var $this = $(this);
        data = $this.data('spritespin');
        $this.unbind('.spritespin');
        $this.removeData('spritespin');
      });
    },
    // Updates a single frame to the specified frame number. If no value is
    // given this will increment the current frame counter.
    // Triggers the onFrame event
    update : function(frame, reverse){
      return this.each(function(){
        var $this = $(this);
        data = $this.data('spritespin');
        settings = data.settings;

        if (reverse != undefined){
          settings.reverse = reverse;
        }

        // update frame counter
        if (frame == undefined){
          settings.frame = (settings.frame + (settings.reverse ? -1 : 1));
        } else {
          settings.frame = frame;
        }
        settings.frame = helper.wrapValue(settings.frame, 0, settings.frames);
        data.target.trigger("onFrame", data);
      });
    },
    // Starts or stops the animation depend on the animate paramter.
    // In case when animation is already running pass "false" to stop.
    // In case when animation is not running pass "true" to start.
    // To keep animation running forever pass "true" for the loop parameter.
    // To detect whether the animation is running or not, do not pass any
    // parameters.
    animate : function(animate, loop){
      if (animate == undefined){
        return $(this).data('spritespin').animation != null;
      } else {
        return this.each(function(){
          var $this = $(this);
          data = $this.data('spritespin');
          settings = data.settings;

          // check the loop variable and update settings
          if (typeof(loop) == "boolean"){
            settings.loop = loop;
          }

          // toggle and update animation settings
          if (animate == "toggle"){
            animate = !settings.animate
            settings.animate = animate;
          } else {
            settings.animate = animate;
          }

          if (animate && data.animation == null){
            // start animation
            data.animation = setInterval(
              function(){
                try {
                  methods.update.apply($this, []);
                } catch(err){
                  // The try catch block is a hack for Opera Browser
                }
              }, data.frameTime);
          } else if (!animate && data.animation != null) {
            // stop animation
            clearInterval(data.animation);
            data.animation = null;
          }
        });
      }
    },
    // Gets the current framenumber when no parameters are passed or
    // updates the player to the sepcified framenumber.
    frame : function(frame){
      if (frame == undefined){
        return $(this).data('spritespin').settings.frame;
      } else {
        return this.each(function(){
          methods.update.apply($(this), [frame]);
        });
      }
    },
    // Gets a value indication whether the animation is looped or not when
    // no parameters are passed. Otherwise sets the loop setting and starts
    // the animation when settings.animate is set to true
    loop : function(value){
      if (value == undefined){
        return $(this).data('spritespin').settings.loop;
      } else {
        return this.each(function(){
          var $this = $(this);
          data = $this.data('spritespin');
          methods.animate.apply($(this), [data.settings.animate, value]);
        });
      }
    },
  };

  var helper = {
    reconfiger : function(instance, data){
      helper.blankBackground(instance, data);
      helper.preloadImages(instance, data, function(){
        helper.updateBackground(instance, data);
        helper.hookSlider(instance, data);
        helper.rebindEvents(instance, data);
        if (data.settings.animate){
          methods.animate.apply(instance, [data.settings.animate, data.settings.loop]);
        }
        instance.trigger("onLoad", data);
      });
    },
    wrapValue : function(value, min, max){
      while (value >= max){
        value -= max;
      }
      while (value < min){
        value += max;
      }
      return value;
    },
    blankBackground : function(instance, data){
      image = "none";
      if (data.settings.preloadBackground != undefined){
        image = ["url('", data.settings.preloadBackground, "')"].join("");
      }
      instance.css({
        width      : [data.settings.width, "px"].join(""),
        height     : [data.settings.height, "px"].join(""),
        "background-image"    : image,
        "background-repeat"   : "repeat-x",
        "background-position" : "0px 0px"
      });
    },
    updateBackground : function(instance){
      var data = instance.data("spritespin");
      var image = data.settings.image;
      var x = data.settings.offsetX;
      var y = -data.settings.offsetY;

      if (typeof(data.settings.image) == "string"){
        if (data.settings.frameStep != undefined){
          x -= (data.settings.frame * data.settings.frameStep);
        } else {
          x -= (data.settings.frame * data.settings.width);
        }
      } else {
        image = data.settings.image[data.settings.frame];
      }

      instance.css({
        width      : [data.settings.width, "px"].join(""),
        height     : [data.settings.height, "px"].join(""),
        "background-image"    : ["url('", image, "')"].join(""),
        "background-repeat"   : "repeat-x",
        "background-position" : [x, "px ", y, "px"].join("")
      });
    },
    hookSlider : function(instance, data){
      if (data.settings.slider != undefined){
        data.settings.slider.slider({
          value   : data.settings.frame,
          min     : 0,
          max     : (data.settings.frames) - 1,
          step    : 1,
          slide   : function(event, ui) {
            methods.animate.apply(instance, [false]);    // stop animation
            methods.frame.apply(instance, [ui.value]);  // update to frame
          },
        });
      }
    },
    rebindEvents : function(instance, data){
      // unbind all events
      instance.unbind('.spritespin');

      // rebind interaction events
      if (data.touchable){
        instance.bind('touchstart.spritespin', behavior[data.settings.behavior].mousedown);
        instance.bind('touchmove.spritespin',  behavior[data.settings.behavior].mousemove);
        instance.bind('touchend.spritespin',   behavior[data.settings.behavior].mouseup);
      }
      instance.bind('mousedown.spritespin',  behavior[data.settings.behavior].mousedown);
      instance.bind('mousemove.spritespin',  behavior[data.settings.behavior].mousemove);
      instance.bind('mouseup.spritespin',    behavior[data.settings.behavior].mouseup);
      instance.bind('mouseenter.spritespin',  behavior[data.settings.behavior].mouseenter);
      instance.bind('mouseover.spritespin',  behavior[data.settings.behavior].mouseover);
      instance.bind('mouseleave.spritespin', behavior[data.settings.behavior].mouseleave);
      instance.bind('onFrame.spritespin', behavior[data.settings.behavior].onFrame);

      // disable selection
	    instance.bind("mousedown.spritespin selectstart.spritespin",
	    		function( event ) { event.preventDefault(); }
	    );

	    instance.bind("onFrame.spritespin", function(event, data){
	      helper.updateBackground(data.target, data);

        // stop animation if we are back at frame 0
        if (data.settings.frame == data.settings.loopFrame && !data.settings.loop){
          methods.animate.apply(data.target, [false]);
        }

        // update the jquery-ui slider
        if (data.settings.slider != undefined){
          data.settings.slider.slider("value", data.settings.frame);
        }
	    });

	    // bind custom events
	    if (data.settings.onFrame != undefined){
	      instance.bind("onFrame.spritespin", data.settings.onFrame);
	    }

	    if (data.settings.onLoad != undefined){
	      instance.bind("onLoad.spritespin", data.settings.onLoad);
	    }
    },
    preloadImages : function(instance, data, callback) {
      var preload = $('<div class="preload"/>');
      if (instance.find(".preload").length == 0){
        instance.append(preload);
      }
      preload.html("Loading");

      var i = 0;
      var total = 1;
      var images = [data.settings.image];

      if (typeof(data.settings.image) == "object"){
        total = data.settings.image.length;
        images = data.settings.image;
      }

      imageObj = new Image();

      for(i = 0; i < total; i++){
        if (i == (total - 1)){
          imageObj.onload = function(){
            instance.find(".preload").detach();
            callback.apply([instance, data]);
          }
        }
        imageObj.src=images[i];
      }
    },
  };

  var behavior = {
    helper : {
      storePoints : function(e, data){
        data.oldX = data.currentX;
        data.oldY = data.currentY;

        if (e.touches != undefined && e.touches.length > 0){
          data.currentX = e.touches[0].clientX;
          data.currentY = e.touches[0].clientY;
        } else {
          data.currentX = e.clientX;
          data.currentY = e.clientY;
        }

        if (data.startX == undefined || data.startY == undefined){
          data.startX = data.currentX;
          data.startY = data.currentY;
          data.clickframe = data.settings.frame;
        }

        if (data.oldX == undefined || data.oldY == undefined){
          data.oldX = data.currentX;
          data.oldY = data.currentY;
        }

        data.dX = data.currentX - data.startX;
        data.dY = data.currentY - data.startY;

        data.ddX = data.currentX - data.oldX;
        data.ddY = data.currentY - data.oldY;
        return false;
      },
      resetPoints : function(e, data){
        data.startX = undefined;
        data.startY = undefined;
        data.currentX = undefined;
        data.currentY = undefined;
        data.oldX = undefined;
        data.oldY = undefined;
        data.dX = 0;
        data.dY = 0;
        data.ddX = 0;
        data.ddY = 0;
      },
      clamp : function(value, min, max){
        return (value > max ? max : (value < min ? min : value));
      }
    },

    none : {
      mousedown  : function(e){ return false; },
      mousemove  : function(e){ return false; },
      mouseup    : function(e){ return false; },

      mouseenter : function(e){ return false; },
      mouseover  : function(e){ return false; },
      mouseleave : function(e){ return false; },

      onFrame : function(e, frame){ return false; }
    },
    spin : {
      mousedown  : function(e){
        var $this = $(this), data = $this.data('spritespin');
        behavior.helper.storePoints(e, data);
        data.onDrag = true;
        return false;
      },
      mousemove  : function(e){
        var $this = $(this), data = $this.data('spritespin');
        if (data.onDrag){
          // perform default drag behavior
          behavior.helper.storePoints(e, data);
          d = data.dX / data.settings.width;
          dFrame = d * data.settings.frames * data.settings.sense;
          frame = Math.round(data.clickframe + dFrame);

          methods.update.apply($this, [frame]);     // update to frame
          methods.animate.apply($this, [false]);  // stop animation

          // calculate framtetime for spinwheel
          if (data.ddX != 0){
            d = data.ddX / data.settings.width;
            dFrame = d * data.settings.frames * data.settings.sense;
            data.frameTime = (data.settings.frameTime / dFrame);
            data.settings.reverse = (data.ddX < 0);
          }
        }
        return false;
      },
      mouseup    : function(e){
        var $this = $(this), data = $this.data('spritespin');
        if (data.onDrag){
          data.onDrag = false;
          $(this).spritespin("animate", true);
        }
        return false;
      },

      mouseenter : function(e){ return false; },
      mouseover  : function(e){ return false; },
      mouseleave : function(e){
        var $this = $(this), data = $this.data('spritespin');
        if (data.onDrag){
          data.onDrag = false;
          $(this).spritespin("animate", true);
        }
        return false;
      },
      onFrame : function(e, data){
        if (data.ddX != 0){
          data.frameTime = data.frameTime + 1;

          $(this).spritespin("animate", false);
          if (data.frameTime < 62){
            $(this).spritespin("animate", true);
          }
        } else {
          $(this).spritespin("animate", false);
        }
        return false;
      }
    },
    drag : {
      mousedown  : function(e){
        var $this = $(this), data = $this.data('spritespin');
        behavior.helper.storePoints(e, data);
        data.onDrag = true;
        data.cachedAnimate = $this.spritespin("animate");
        return false;
      },
      mousemove  : function(e){
        var $this = $(this), data = $this.data('spritespin');
        if (data.onDrag){
          behavior.helper.storePoints(e, data);
          d = data.dX / data.settings.width;
          dFrame = d * data.settings.frames * data.settings.sense;
          frame = Math.round(data.clickframe + dFrame);

          methods.update.apply($this, [frame]);     // update to frame
          methods.animate.apply($(this), [false]);  // stop animation
        }
        return false;
      },
      mouseup    : function(e){
        var $this = $(this), data = $this.data('spritespin');
        behavior.helper.resetPoints(e, data);
        data.onDrag = false;
        methods.animate.apply($(this), [data.cachedAnimate]);
        return false;
      },

      mouseenter : function(e){ return false; },
      mouseover  : function(e){ return false; },
      mouseleave : function(e){
        var $this = $(this), data = $this.data('spritespin');
        behavior.helper.resetPoints(e, data);
        data.onDrag = false;
        methods.animate.apply($(this), [data.cachedAnimate]);
        return false;
      },

      onFrame : function(e, frame){ return false; }
    },
  };
})(jQuery);
<|endoftext|>


<commit_before><commit_msg>add jsx-a11y rules<commit_after>'use strict'

module.exports = {
  plugins: [
    'jsx-a11y',
  ],
  rules: {
    'jsx-a11y/aria-props': 2,
    'jsx-a11y/aria-proptypes': 2,
    'jsx-a11y/aria-role': 2,
    'jsx-a11y/aria-unsupported-elements': 2,
    'jsx-a11y/href-no-hash': 0,
    'jsx-a11y/img-has-alt': 2,
    'jsx-a11y/img-redundant-alt': 2,
    'jsx-a11y/label-has-for': 2,
    'jsx-a11y/mouse-events-have-key-events': 2,
    'jsx-a11y/no-access-key': 2,
    'jsx-a11y/no-onchange': 2,
    'jsx-a11y/onclick-has-focus': 2,
    'jsx-a11y/onclick-has-role': 2,
    'jsx-a11y/role-has-required-aria-props': 2,
    'jsx-a11y/role-supports-aria-props': 2,
    'jsx-a11y/tabindex-no-positive': 0,
  },
}
<|endoftext|>


<commit_before>define(["dojo/_base/declare", "./Value",
        "ppwcode/oddsAndEnds/js", "dojo/i18n", "dojo/_base/kernel", "dojo/_base/lang", "module"],
  function(declare, Value,
           js, i18n, kernel, lang, module) {

    var EnumerationValue = declare([Value], {
      // summary:
      //   Support for enum types.
      //   Values of enum types are communicated to and from the server as Strings in JSON.
      // description:
      //   Enumeration types should be defined as a hash of the EnumerationValues.
      //   This class thus defines the values, but not the type.
      //   This hash is referenced with a Capitalized name, like a Constructor (although it is an object,
      //   and not a function).
      //   An enumeration value can have a label (a human representation) that is different in different
      //   languages. To enable this, place a set of nls files in the nls directory next to the module
      //   defining the enumeration type with the same name as the module itself (or define the
      //   name used in `bundleName`). The Constructor needs to have a property `mid` containing the
      //   module id for this to work. The EnumerationValue Constructor then has a `format` and `parse`
      //   method, that can take an options-argument that has a locale in the regular way.
      //   If we don't find a locale in the options, we use the default locale.

      _c_invar: [
        function() {return js.typeOf(this.toJSON()) === "string" && this.toJSON() != "";}
      ],

      // _representation: String
      //   The internal representation of the value.
      //   This string is used in communication to and from the server.
      _representation: null,

      constructor: function(/*Object*/ kwargs) {
        this._c_pre(function() {return this._c_prop_mandatory(kwargs, "representation");});
        this._c_pre(function() {return this._c_prop_string(kwargs, "representation");});

        this._representation = kwargs.representation;
      },

      isValueOf: function(/*Object*/ EnumDef) {
        // summary:
        //   Is this defined in `EnumDef`?
        // description:
        //   Similar to isInstanceOf.
        //   Note: with the current implementation of declare, we cannot overwrite isInstanceOf.
        //   (the declare definition of isInstanceOf overwrites anything we declare).

        return Object.keys(EnumDef).some(function(ed) {return EnumDef[ed];});
      },

      equals: function(/*EnumerationValue*/ other) {
        // summary:
        //   Referential equality.

        return this.inherited(arguments) && this === other;
      },

      getValue: function() {
        return this._representation;
      },

      toJSON: function() {
        return this._representation;
      },

      compare: function(other) {
        // summary:
        //   Comparison based on getValue. Can be overriden.

        return this.equals(other) ? 0 : (this.getValue() < other.getValue() ? -1 : +1);
      },

      toString: function() {
        return this._representation;
      },

      getLabel: function(/*String*/ lang) {
        // summary:
        //   Shortcut to EnumerationValue.format(this, {locale: lang});

        return EnumerationValue.format(this, {locale: lang});
      }

    });

    function values(EnumDef) {
      // summary:
      //   The values of EnumDef as a an array.

      if (!EnumDef._values) {
        EnumDef._values = Object.keys(EnumDef).
          filter(function(key) {return key != "superclass" && EnumDef[key] && EnumDef[key].isInstanceOf && EnumDef[key].isInstanceOf(EnumerationValue)}).
          map(function(key) {return EnumDef[key];});
      }
      return EnumDef._values;
    }

    function isEnumJson(EnumDef, json) {
      // summary:
      //   Is `json` the String representation of a value defined in EnumDef?

      return values(EnumDef).some(function(ev) {return ev._representation === json;});
    }

    function enumRevive(EnumDef, json) {
      // summary:
      //   Revive a json String value into the appropriate
      //   EnumDef value.
      // description:
      //   Returns undefined if no such value is found.
      //   *Note that `enum` is a reserved word.*
      //   https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Reserved_Words

      // pre: json is a String;
      // pre: isEnumJson(EnumDef, json);

      if (!json) {
        return undefined;
      }
      var match = Object.keys(EnumDef).filter(function(ed) {return EnumDef[ed]._representation === json;});
      if (match.length > 1) {
        throw "Error: there are different values in enum type " + EnumDef + " with the same value.";
      }
      if (match.length < 1) {
        return undefined;
      }
      return EnumDef[match[0]]; // return EnumerationValue
    }

    function dirFromMid(mid) {
      // summary:
      //   Helper function to get the directory from a MID

      var parts = mid.split("/");
      parts.pop();
      return parts.join("/");
    }

    function getParentDirectory(/*Function*/ EnumValueConstructor) {
      if (!EnumValueConstructor._parentDirectory) {
        if (!EnumValueConstructor.mid) {
          throw "ERROR you must defined a property `mid` on the enumeration value constructor";
        }
        EnumValueConstructor._parentDirectory = dirFromMid(EnumValueConstructor.mid);
      }
      return EnumValueConstructor._parentDirectory;
    }

    function getBundleName(/*Function*/ EnumValueConstructor) {
      return EnumValueConstructor.bundleName || EnumValueConstructor.mid.split("/").pop();
    }

    function getBundle(/*Function*/ EnumValueConstructor, /*String*/ lang) {
      return i18n.getLocalization(
        getParentDirectory(EnumValueConstructor),
        getBundleName(EnumValueConstructor),
        lang
      );
    }

    function format(v, /*Object*/ options) {
      // summary:
      //   options.locale can be filled out; if not, the default locale is used.
      //   If no label is found, the representation itself is returned.

      if (!v) {
        return null;
      }
      var lang = options.locale || kernel.locale;
      var result = getBundle(v.constructor, lang)[v.toJSON()];
      if (!result && result != "") {
        return v.toJSON();
      }
      else {
        return result;
      }
    }

    function parse(/*Function*/ EnumValueConstructor, /*String*/ str, /*Object*/ options) {
      // summary:
      //   options.locale can be filled out; if not, the default locale is used.
      //   If no label is found, the representation itself is returned.

      if (!str && str !== "") {
        return null;
      }
      var lang = options.locale || kernel.locale;
      var bundle = getBundle(EnumValueConstructor, lang);
      for (var representation in bundle) {
        if (bundle[representation] === str) {
          return enumRevive(EnumValueConstructor, representation);
        }
      }
      return undefined;
    }

    function methodFactory(/*Function*/ EnumValueConstructor, /*Function*/ f) {
      return lang.partial(f, EnumValueConstructor);
    }

    function enumDeclare(/*Object*/ prototypeDef, /*Array*/ valueDefinitions, /*module*/ mod, /*String*/ bundleName) {
      var Enum = declare([EnumerationValue], prototypeDef);
      Enum._values = [];
      valueDefinitions.forEach(function(vDef) {
        var def = js.typeOf(vDef) === "string" ? {representation: vDef} : vDef;
        Enum[vDef] = new Enum(def);
        Enum._values.push(Enum[vDef]);
      });
      Enum.isJson = methodFactory(Enum, isEnumJson);
      Enum.revive = methodFactory(Enum, enumRevive);
      Enum.values = methodFactory(Enum, values);
      Enum.format = format;
      Enum.parse = methodFactory(Enum, parse);
      if (mod) {
        Enum.mid = mod.id;
      }
      if (bundleName) {
        Enum.bundleName = bundleName;
      }
      return Enum;
    }

    EnumerationValue.mid = module.id;
    EnumerationValue.isJson = isEnumJson;
    EnumerationValue.revive = enumRevive;
    EnumerationValue.generalValues = values;
    EnumerationValue.bundleName = null;
    EnumerationValue.getBundle = getBundle;
    EnumerationValue.format = format;
    EnumerationValue.generalParse = parse;
    EnumerationValue.methodFactory = methodFactory;

    EnumerationValue.declare = enumDeclare;

    return EnumerationValue;
  }
);
<commit_msg>Fixed a major bug (yesterday). This never worked!<commit_after>define(["dojo/_base/declare", "./Value",
        "ppwcode/oddsAndEnds/js", "dojo/i18n", "dojo/_base/kernel", "dojo/_base/lang", "module"],
  function(declare, Value,
           js, i18n, kernel, lang, module) {

    var EnumerationValue = declare([Value], {
      // summary:
      //   Support for enum types.
      //   Values of enum types are communicated to and from the server as Strings in JSON.
      // description:
      //   Enumeration types should be defined as a hash of the EnumerationValues.
      //   This class thus defines the values, but not the type.
      //   This hash is referenced with a Capitalized name, like a Constructor (although it is an object,
      //   and not a function).
      //   An enumeration value can have a label (a human representation) that is different in different
      //   languages. To enable this, place a set of nls files in the nls directory next to the module
      //   defining the enumeration type with the same name as the module itself (or define the
      //   name used in `bundleName`). The Constructor needs to have a property `mid` containing the
      //   module id for this to work. The EnumerationValue Constructor then has a `format` and `parse`
      //   method, that can take an options-argument that has a locale in the regular way.
      //   If we don't find a locale in the options, we use the default locale.

      _c_invar: [
        function() {return js.typeOf(this.toJSON()) === "string" && this.toJSON() != "";}
      ],

      // _representation: String
      //   The internal representation of the value.
      //   This string is used in communication to and from the server.
      _representation: null,

      constructor: function(/*Object*/ kwargs) {
        this._c_pre(function() {return this._c_prop_mandatory(kwargs, "representation");});
        this._c_pre(function() {return this._c_prop_string(kwargs, "representation");});

        this._representation = kwargs.representation;
      },

      isValueOf: function(/*Object*/ EnumDef) {
        // summary:
        //   Is this defined in `EnumDef`?
        // description:
        //   Similar to isInstanceOf.
        //   Note: with the current implementation of declare, we cannot overwrite isInstanceOf.
        //   (the declare definition of isInstanceOf overwrites anything we declare).

        return Object.keys(EnumDef).some(function(ed) {return EnumDef[ed];});
      },

      equals: function(/*EnumerationValue*/ other) {
        // summary:
        //   Referential equality.

        return this.inherited(arguments) && this === other;
      },

      getValue: function() {
        return this._representation;
      },

      toJSON: function() {
        return this._representation;
      },

      compare: function(other) {
        // summary:
        //   Comparison based on getValue. Can be overriden.

        return this.equals(other) ? 0 : (this.getValue() < other.getValue() ? -1 : +1);
      },

      toString: function() {
        return this._representation;
      },

      getLabel: function(/*String*/ lang) {
        // summary:
        //   Shortcut to EnumerationValue.format(this, {locale: lang});

        return EnumerationValue.format(this, {locale: lang});
      }

    });

    function values(EnumDef) {
      // summary:
      //   The values of EnumDef as a an array.

      if (!EnumDef._values) {
        EnumDef._values = Object.keys(EnumDef).
          filter(function(key) {return key != "superclass" && EnumDef[key] && EnumDef[key].isInstanceOf && EnumDef[key].isInstanceOf(EnumerationValue)}).
          map(function(key) {return EnumDef[key];});
      }
      return EnumDef._values;
    }

    function isEnumJson(EnumDef, json) {
      // summary:
      //   Is `json` the String representation of a value defined in EnumDef?

      return values(EnumDef).some(function(ev) {return ev._representation === json;});
    }

    function enumRevive(EnumDef, json) {
      // summary:
      //   Revive a json String value into the appropriate
      //   EnumDef value.
      // description:
      //   Returns undefined if no such value is found.
      //   *Note that `enum` is a reserved word.*
      //   https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Reserved_Words

      // pre: json is a String;
      // pre: isEnumJson(EnumDef, json);

      if (!json) {
        return undefined;
      }
      var match = Object.keys(EnumDef).filter(function(ed) {return EnumDef[ed]._representation === json;});
      if (match.length > 1) {
        throw "Error: there are different values in enum type " + EnumDef + " with the same value.";
      }
      if (match.length < 1) {
        return undefined;
      }
      return EnumDef[match[0]]; // return EnumerationValue
    }

    function dirFromMid(mid) {
      // summary:
      //   Helper function to get the directory from a MID

      var parts = mid.split("/");
      parts.pop();
      return parts.join("/");
    }

    function getParentDirectory(/*Function*/ EnumValueConstructor) {
      if (!EnumValueConstructor._parentDirectory) {
        if (!EnumValueConstructor.mid) {
          throw "ERROR you must defined a property `mid` on the enumeration value constructor";
        }
        EnumValueConstructor._parentDirectory = dirFromMid(EnumValueConstructor.mid);
      }
      return EnumValueConstructor._parentDirectory;
    }

    function getBundleName(/*Function*/ EnumValueConstructor) {
      return EnumValueConstructor.bundleName || EnumValueConstructor.mid.split("/").pop();
    }

    function getBundle(/*Function*/ EnumValueConstructor, /*String*/ lang) {
      return i18n.getLocalization(
        getParentDirectory(EnumValueConstructor),
        getBundleName(EnumValueConstructor),
        lang
      );
    }

    function format(v, /*Object*/ options) {
      // summary:
      //   options.locale can be filled out; if not, the default locale is used.
      //   If no label is found, the representation itself is returned.

      if (!v) {
        return null;
      }
      var lang = options.locale || kernel.locale;
      var result = getBundle(v.constructor, lang)[v.toJSON()];
      if (!result && result != "") {
        return v.toJSON();
      }
      else {
        return result;
      }
    }

    function parse(/*Function*/ EnumValueConstructor, /*String*/ str, /*Object*/ options) {
      // summary:
      //   options.locale can be filled out; if not, the default locale is used.
      //   If no label is found, the representation itself is returned.

      if (!str && str !== "") {
        return null;
      }
      var lang = options.locale || kernel.locale;
      var bundle = getBundle(EnumValueConstructor, lang);
      for (var representation in bundle) {
        if (bundle[representation] === str) {
          return enumRevive(EnumValueConstructor, representation);
        }
      }
      return undefined;
    }

    function methodFactory(/*Function*/ EnumValueConstructor, /*Function*/ f) {
      return lang.partial(f, EnumValueConstructor);
    }

    function enumDeclare(/*Object*/ prototypeDef, /*Array*/ valueDefinitions, /*module*/ mod, /*String*/ bundleName) {
      var Enum = declare([EnumerationValue], prototypeDef);
      Enum._values = [];
      valueDefinitions.forEach(function(vDef) {
        var def = js.typeOf(vDef) === "string" ? {representation: vDef} : vDef;
        Enum[vDef.representation] = new Enum(def);
        Enum._values.push(Enum[vDef]);
      });
      Enum.isJson = methodFactory(Enum, isEnumJson);
      Enum.revive = methodFactory(Enum, enumRevive);
      Enum.values = methodFactory(Enum, values);
      Enum.format = format;
      Enum.parse = methodFactory(Enum, parse);
      if (mod) {
        Enum.mid = mod.id;
      }
      if (bundleName) {
        Enum.bundleName = bundleName;
      }
      return Enum;
    }

    EnumerationValue.mid = module.id;
    EnumerationValue.isJson = isEnumJson;
    EnumerationValue.revive = enumRevive;
    EnumerationValue.generalValues = values;
    EnumerationValue.bundleName = null;
    EnumerationValue.getBundle = getBundle;
    EnumerationValue.format = format;
    EnumerationValue.generalParse = parse;
    EnumerationValue.methodFactory = methodFactory;

    EnumerationValue.declare = enumDeclare;

    return EnumerationValue;
  }
);
<|endoftext|>


<commit_before>/*
  Aras.HTML5 provides a HTML5 client library to build Aras Innovator Applications

  Copyright (C) 2015 Processwall Limited.

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

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

  You should have received a copy of the GNU Affero General Public License
  along with this program.  If not, see http://opensource.org/licenses/AGPL-3.0.

  Company: Processwall Limited
  Address: The Winnowing House, Mill Lane, Askham Richard, York, YO23 3NW, United Kingdom
  Tel:     +44 113 815 3440
  Email:   support@processwall.com
*/

define([
	'dojo/_base/declare',
	'dojo/_base/lang',
	'dojo/dom-construct',
	'dojo/_base/array',
	'./Control',
	'./Properties/String',
	'./Properties/List',
	'./Properties/Float',
	'./Properties/Integer',
	'./Properties/Sequence'
], function(declare, lang, construct, array, Control, String, List, Float, Integer, Sequence) {

	return declare('Aras.View.Cell', [Control], {

		Column: null,

		Row: null,

		Value: null,

		Node: null,

		_viewModelValueHandle: null,

		_startup: function() {
			this.inherited(arguments);

			// Update Cell
			this._updateCell();
		},

		_destroy: function() {
			this.inherited(arguments);

			if (this._viewModelValueHandle)
			{
				this._viewModelValueHandle.unwatch();
			}
		},

		OnViewModelChanged: function(name, oldValue, newValue) {
			this.inherited(arguments);

			// Update Cell
			this._updateCell();
		},

		_updateCell: function() {

			if (this.ViewModel != null)
			{
				// Watch for changes in ViewModel Value
				if (this._viewModelValueHandle)
				{
					this._viewModelValueHandle.unwatch();
				}

				this._viewModelValueHandle = this.ViewModel.watch("Value", lang.hitch(this, this.OnViewModelValueChange));
			}
		},

		OnViewModelValueChange: function(name, oldValue, newValue) {

			if (this.Value)
			{
				// Update Value ViewModel
				this.Value.set("ViewModel", newValue);
			}
		},

		RenderCell: function() {

			if ((this.ViewModel != null) && (this.ViewModel.Value != null))
			{
				if ( dojo.isIE)
				{
					if (this.Value != null)
					{
						this.Value = null;
					}
				}

				if (this.Value == null)
				{
					// Need to create new Widget
					if (this.ViewModel.Value.Type == 'Aras.View.Properties.Boolean')
					{
						this.Value = this.ViewModel.Session.ViewControl(this.ViewModel.Value);
					}
					else
					{
						this.Value = this.ViewModel.Session.ViewControl(this.ViewModel.Value, {style: 'width:100%; padding:0; margin:1px; border:0'});
					}

					// Start Control
					this.Value.startup();
				}

				return this.Value;
			}
			else
			{
				return null;
			}
		}

	});
});<commit_msg>Updated Grid Style<commit_after>/*
  Aras.HTML5 provides a HTML5 client library to build Aras Innovator Applications

  Copyright (C) 2015 Processwall Limited.

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

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

  You should have received a copy of the GNU Affero General Public License
  along with this program.  If not, see http://opensource.org/licenses/AGPL-3.0.

  Company: Processwall Limited
  Address: The Winnowing House, Mill Lane, Askham Richard, York, YO23 3NW, United Kingdom
  Tel:     +44 113 815 3440
  Email:   support@processwall.com
*/

define([
	'dojo/_base/declare',
	'dojo/_base/lang',
	'dojo/dom-construct',
	'dojo/_base/array',
	'./Control',
	'./Properties/String',
	'./Properties/List',
	'./Properties/Float',
	'./Properties/Integer',
	'./Properties/Sequence'
], function(declare, lang, construct, array, Control, String, List, Float, Integer, Sequence) {

	return declare('Aras.View.Cell', [Control], {

		Column: null,

		Row: null,

		Value: null,

		Node: null,

		_viewModelValueHandle: null,

		_startup: function() {
			this.inherited(arguments);

			// Update Cell
			this._updateCell();
		},

		_destroy: function() {
			this.inherited(arguments);

			if (this._viewModelValueHandle)
			{
				this._viewModelValueHandle.unwatch();
			}
		},

		OnViewModelChanged: function(name, oldValue, newValue) {
			this.inherited(arguments);

			// Update Cell
			this._updateCell();
		},

		_updateCell: function() {

			if (this.ViewModel != null)
			{
				// Watch for changes in ViewModel Value
				if (this._viewModelValueHandle)
				{
					this._viewModelValueHandle.unwatch();
				}

				this._viewModelValueHandle = this.ViewModel.watch("Value", lang.hitch(this, this.OnViewModelValueChange));
			}
		},

		OnViewModelValueChange: function(name, oldValue, newValue) {

			if (this.Value)
			{
				// Update Value ViewModel
				this.Value.set("ViewModel", newValue);
			}
		},

		RenderCell: function() {

			if ((this.ViewModel != null) && (this.ViewModel.Value != null))
			{
				if ( dojo.isIE)
				{
					if (this.Value != null)
					{
						this.Value = null;
					}
				}

				if (this.Value == null)
				{
					// Need to create new Widget
					if (this.ViewModel.Value.Type == 'Aras.View.Properties.Boolean')
					{
						this.Value = this.ViewModel.Session.ViewControl(this.ViewModel.Value);
					}
					else
					{
						this.Value = this.ViewModel.Session.ViewControl(this.ViewModel.Value, {style: 'width:100%; padding:0; margin:2px; border:0'});
					}

					// Start Control
					this.Value.startup();
				}

				return this.Value;
			}
			else
			{
				return null;
			}
		}

	});
});<|endoftext|>


<commit_before>/*
  Copyright 2017 Processwall Limited

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

  Company: Processwall Limited
  Address: The Winnowing House, Mill Lane, Askham Richard, York, YO23 3NW, United Kingdom
  Tel:     +44 113 815 3440
  Web:     http://www.processwall.com
  Email:   support@processwall.com
*/

define([
	'dojo/_base/declare',
	'dojo/_base/lang',
	'dojo/_base/array',
	'./_Grid',
	'dgrid/util/misc',
	'dijit/layout/BorderContainer',
	'./Control',
	'./CellEditors/Boolean',
	'./CellEditors/Date',
	'./CellEditors/Decimal',
	'./CellEditors/Float',
	'./CellEditors/Integer',
	'./CellEditors/Item',
	'./CellEditors/List',
	'./CellEditors/String',
	'./CellEditors/Text',
	'./Column',
	'./Row',
	'./Cell',
], function(declare, lang, array, _Grid, GridMisc, BorderContainer, Control, BooleanEditor, DateEditor, DecimalEditor, FloatEditor, IntegerEditor, ItemEditor, ListEditor, StringEditor, TextEditor, Column, Row, Cell) {

	return declare('Aras.View.Grid', [BorderContainer, Control], {

		_grid: null,

		_rowsHandle: null,

		_columnsHandle: null,

		SelectedRows: null,

		baseClass: 'dijitContentPaneNoPadding',

		ShowHeader: null,

		Columns: null,

		Rows: null,

		constructor: function() {

			this.Columns = [];
			this.Rows = [];
		},

		startup: function() {
			this.inherited(arguments);

			// Call Control Startup
			this._startup();

			// Create Grid
			this._grid = new _Grid({ region: 'center', selectionMode: 'extended', showHeader: this.ShowHeader });
			this.addChild(this._grid);

			// Process Grid Select Event
			this._grid.on('dgrid-select', lang.hitch(this, function(event) {

				if ((this.ViewModel != null) && (this.ViewModel.Select.CanExecute))
				{
					var Parameters = [];

					array.forEach(event.rows, function(row) {
						Parameters.push(this.ViewModel.Rows[row.data.id].ID);
					}, this);

					this.ViewModel.Select.Execute(Parameters);
				}
			}));

			// Process Grid Editor Show Event
			this._grid.on('dgrid-editor-show', lang.hitch(this, function(event) {

				if (this.ViewModel != null)
				{
					// Get Cell ViewModel
					var rowid = event.cell.row.id;
					var colname = event.cell.column.id;
					var rowviewmodel = this.ViewModel.Rows[rowid];
					var colindex = -1;

					for (var j=0; j<this.ViewModel.Columns.length; j++)
					{
						if (this.ViewModel.Columns[j].Name == colname)
						{
							colindex = j;
							break;
						}
					}

					// Set Editor ViewModel
					event.editor.set('ViewModel', this.ViewModel.Rows[rowid].Cells[colindex]);
				}

			}));

			// Process Grid Editor Changed Event
			this._grid.on('dgrid-datachange', lang.hitch(this, function(event) {

				// Cancel Event
				event.preventDefault();

				if (this.ViewModel != null)
				{
					// Get Cell ViewModel
					var rowid = event.cell.row.id;
					var colname = event.cell.column.id;
					var rowviewmodel = this.ViewModel.Rows[rowid];
					var colindex = -1;

					for (var j=0; j<this.ViewModel.Columns.length; j++)
					{
						if (this.ViewModel.Columns[j].Name == colname)
						{
							colindex = j;
							break;
						}
					}

					// Write New Value
					this.ViewModel.Rows[rowid].Cells[colindex].Write();
				}

			}));

			// Watch for Changes in Columns
			this._columnsHandle = this.ViewModel.watch("Columns", lang.hitch(this, this._updateColumns));

			// Watch for Changes in Rows
			this._rowsHandle = this.ViewModel.watch("Rows", lang.hitch(this, this._updateRows));

			// Update Rows
			this._updateColumns();
		},

		destroy: function() {
			this.inherited(arguments);

			// Call Control Destroy
			this._destroy();

			if (this._rowsHandle)
			{
				this._rowsHandle.unwatch();
			}

			if (this._columnsHandle)
			{
				this._columnsHandle.unwatch();
			}
		},

		OnViewModelChanged: function(name, oldValue, newValue) {
			this.inherited(arguments);

			// Unwatch for changes in Columns
			if (this._columnsHandle)
			{
				this._columnsHandle.unwatch();
			}

			// Unwatch for Changes in Rows
			if (this._rowsHandle)
			{
				this._rowsHandle.unwatch();
			}

			// Watch for Changes in Columns
			this._columnsHandle = this.ViewModel.watch("Columns", lang.hitch(this, this._updateColumns));

			// Watch for Changes in Rows
			this._rowsHandle = this.ViewModel.watch("Rows", lang.hitch(this, this._updateRows));

			// Update Columns
			this._updateColumns();
		},

		_updateColumns: function() {

			// Reset Columns
			array.forEach(this.Columns, function(column) {
				column.Visible = false;
			}, this);

			// Create Columns
			var gridcolumns = {};

			array.forEach(this.ViewModel.Columns, function(columnviewmodel, i) {

				if (this.Columns[i])
				{
					this.Columns[i].Visible = true;
				}
				else
				{
					this.Columns[i] = new Column({ Grid: this});
					this.Columns[i].startup();
				}

				this.Columns[i].ViewModel = columnviewmodel;

				gridcolumns[columnviewmodel.Name] = {};
				gridcolumns[columnviewmodel.Name].label = columnviewmodel.Label;
				gridcolumns[columnviewmodel.Name].sortable = false;
				gridcolumns[columnviewmodel.Name].editOn = 'click';
				gridcolumns[columnviewmodel.Name].dismissOnEnter = false;

				if (columnviewmodel.Editable)
				{
					switch(columnviewmodel.Type)
					{
						case "Aras.View.Columns.Boolean":
							gridcolumns[columnviewmodel.Name].editor = BooleanEditor;
							break;

						case "Aras.View.Columns.Date":
							gridcolumns[columnviewmodel.Name].editor = DateEditor;
							gridcolumns[columnviewmodel.Name].editorArgs = { style: 'width:' + (columnviewmodel.Width - 5) + 'px;' };
							break;

						case "Aras.View.Columns.Decimal":
							gridcolumns[columnviewmodel.Name].editor = DecimalEditor;
							break;

						case "Aras.View.Columns.Float":
							gridcolumns[columnviewmodel.Name].editor = FloatEditor;
							break;

						case "Aras.View.Columns.Integer":
							gridcolumns[columnviewmodel.Name].editor = IntegerEditor;
							break;

						case "Aras.View.Columns.Item":
							gridcolumns[columnviewmodel.Name].editor = ItemEditor;
							break;

						case "Aras.View.Columns.List":
							gridcolumns[columnviewmodel.Name].editor = ListEditor;
							break;

						case "Aras.View.Columns.String":
							gridcolumns[columnviewmodel.Name].editor = StringEditor;
							break;

						case "Aras.View.Columns.Text":
							gridcolumns[columnviewmodel.Name].editor = TextEditor;
							break;

						default:
							gridcolumns[columnviewmodel.Name].editor = undefined;
							break;
					}
				}
				else
				{
					gridcolumns[columnviewmodel.Name].editor = undefined;
				}

				// Cell Formatter
				gridcolumns[columnviewmodel.Name].formatter = lang.hitch(this, this._formatCell);

				// Set Column Width
				GridMisc.addCssRule('#' + GridMisc.escapeCssIdentifier(this._grid.domNode.id) +
						' .dgrid-column-' + GridMisc.escapeCssIdentifier(columnviewmodel.Name, '-'),
						'width: ' + columnviewmodel.Width + 'px;');

			}, this);

			// Set Grid Columns
			this._grid._setColumns(gridcolumns);

			// Update Rows
			this._updateRows();
		},

		_updateRows: function() {

			// Reset Rows
			array.forEach(this.Rows, function(row) {
				row.Visible = false;
			}, this);

			// Render Grid
			var rowdata = [];

			array.forEach(this.ViewModel.Rows, function(rowviewmodel, i) {

				if (this.Rows[i])
				{
					this.Rows[i].Visible = true;
				}
				else
				{
					this.Rows[i] = new Row({ Grid: this });
					this.Rows[i].startup();
				}

				this.Rows[i].ViewModel = rowviewmodel;
				this.Rows[i].ID = i;

				rowdata[i] = new Object();
				rowdata[i]['id'] = i;

				for (var j=0; j<this.ViewModel.Columns.length; j++)
				{
					if (!this.Rows[i].Cells[j])
					{
						this.Rows[i].Cells[j] = new Cell({ Row: this.Rows[i], Column: this.Columns[j]});
						this.Rows[i].Cells[j].startup();
					}

					this.Rows[i].Cells[j].set('ViewModel', rowviewmodel.Cells[j]);

					rowdata[i][this.ViewModel.Columns[j].Name] = this.Rows[i].Cells[j];
				}

			}, this);

			// Clear Grid
			this._grid.refresh();

			// Render Grid
			this._grid.renderArray(rowdata);
		},

		_formatCell: function(value, object) {

			switch(value.Column.ViewModel.Type)
			{
				case "Aras.View.Columns.Boolean":

					if (value.ViewModel.Value == '1')
					{
						return '<input type="checkbox" disabled checked="checked">';
					}
					else
					{
						return '<input type="checkbox" disabled>';
					}
				case "Aras.View.Columns.List":

					if (value.ViewModel.Selected != null)
					{
						return value.ViewModel.Selected.Label;
					}
					else
					{
						return '';
					}

				default:

					if (value.ViewModel.Value != null)
					{
						return value.ViewModel.Value;
					}
					else
					{
						return '';
					}
			}

		}

	});
});<commit_msg>Implemented row by row Grid editing<commit_after>/*
  Copyright 2017 Processwall Limited

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

  Company: Processwall Limited
  Address: The Winnowing House, Mill Lane, Askham Richard, York, YO23 3NW, United Kingdom
  Tel:     +44 113 815 3440
  Web:     http://www.processwall.com
  Email:   support@processwall.com
*/

define([
	'dojo/_base/declare',
	'dojo/_base/lang',
	'dojo/_base/array',
	'./_Grid',
	'dgrid/util/misc',
	'dijit/layout/BorderContainer',
	'./Control',
	'./CellEditors/Boolean',
	'./CellEditors/Date',
	'./CellEditors/Decimal',
	'./CellEditors/Float',
	'./CellEditors/Integer',
	'./CellEditors/Item',
	'./CellEditors/List',
	'./CellEditors/String',
	'./CellEditors/Text',
	'./Column',
	'./Row',
	'./Cell',
], function(declare, lang, array, _Grid, GridMisc, BorderContainer, Control, BooleanEditor, DateEditor, DecimalEditor, FloatEditor, IntegerEditor, ItemEditor, ListEditor, StringEditor, TextEditor, Column, Row, Cell) {

	return declare('Aras.View.Grid', [BorderContainer, Control], {

		_grid: null,

		_rowsHandle: null,

		_columnsHandle: null,

		SelectedRows: null,

		baseClass: 'dijitContentPaneNoPadding',

		ShowHeader: null,

		Columns: null,

		Rows: null,

		constructor: function() {

			this.Columns = [];
			this.Rows = [];
		},

		startup: function() {
			this.inherited(arguments);

			// Call Control Startup
			this._startup();

			// Create Grid
			this._grid = new _Grid({ region: 'center', selectionMode: 'extended', showHeader: this.ShowHeader });
			this.addChild(this._grid);

			// Process Grid Select Event
			this._grid.on('dgrid-select', lang.hitch(this, function(event) {

				if ((this.ViewModel != null) && (this.ViewModel.Select.CanExecute))
				{
					var Parameters = [];

					array.forEach(event.rows, function(row) {
						Parameters.push(this.ViewModel.Rows[row.data.id].ID);
					}, this);

					this.ViewModel.Select.Execute(Parameters);
				}
			}));

			// Process Grid Editor Show Event
			this._grid.on('dgrid-editor-show', lang.hitch(this, function(event) {

				if (this.ViewModel != null)
				{
					// Get Cell ViewModel
					var rowid = event.cell.row.id;
					var colname = event.cell.column.id;
					var rowviewmodel = this.ViewModel.Rows[rowid];
					var colindex = -1;

					for (var j=0; j<this.ViewModel.Columns.length; j++)
					{
						if (this.ViewModel.Columns[j].Name == colname)
						{
							colindex = j;
							break;
						}
					}

					// Set Editor ViewModel
					event.editor.set('ViewModel', this.ViewModel.Rows[rowid].Cells[colindex]);
				}

			}));

			// Process Grid Editor Changed Event
			this._grid.on('dgrid-datachange', lang.hitch(this, function(event) {

				// Cancel Event
				event.preventDefault();

				if (this.ViewModel != null)
				{
					// Get Cell ViewModel
					var rowid = event.cell.row.id;
					var colname = event.cell.column.id;
					var rowviewmodel = this.ViewModel.Rows[rowid];
					var colindex = -1;

					for (var j=0; j<this.ViewModel.Columns.length; j++)
					{
						if (this.ViewModel.Columns[j].Name == colname)
						{
							colindex = j;
							break;
						}
					}

					// Write New Value
					this.ViewModel.Rows[rowid].Cells[colindex].Write();
				}

			}));

			// Watch for Changes in Columns
			this._columnsHandle = this.ViewModel.watch("Columns", lang.hitch(this, this._updateColumns));

			// Watch for Changes in Rows
			this._rowsHandle = this.ViewModel.watch("Rows", lang.hitch(this, this._updateRows));

			// Update Rows
			this._updateColumns();
		},

		destroy: function() {
			this.inherited(arguments);

			// Call Control Destroy
			this._destroy();

			if (this._rowsHandle)
			{
				this._rowsHandle.unwatch();
			}

			if (this._columnsHandle)
			{
				this._columnsHandle.unwatch();
			}
		},

		OnViewModelChanged: function(name, oldValue, newValue) {
			this.inherited(arguments);

			// Unwatch for changes in Columns
			if (this._columnsHandle)
			{
				this._columnsHandle.unwatch();
			}

			// Unwatch for Changes in Rows
			if (this._rowsHandle)
			{
				this._rowsHandle.unwatch();
			}

			// Watch for Changes in Columns
			this._columnsHandle = this.ViewModel.watch("Columns", lang.hitch(this, this._updateColumns));

			// Watch for Changes in Rows
			this._rowsHandle = this.ViewModel.watch("Rows", lang.hitch(this, this._updateRows));

			// Update Columns
			this._updateColumns();
		},

		_updateColumns: function() {

			// Reset Columns
			array.forEach(this.Columns, function(column) {
				column.Visible = false;
			}, this);

			// Create Columns
			var gridcolumns = {};

			array.forEach(this.ViewModel.Columns, function(columnviewmodel, i) {

				if (this.Columns[i])
				{
					this.Columns[i].Visible = true;
				}
				else
				{
					this.Columns[i] = new Column({ Grid: this});
					this.Columns[i].startup();
				}

				this.Columns[i].ViewModel = columnviewmodel;

				gridcolumns[columnviewmodel.Name] = {};
				gridcolumns[columnviewmodel.Name].label = columnviewmodel.Label;
				gridcolumns[columnviewmodel.Name].sortable = false;
				gridcolumns[columnviewmodel.Name].editOn = 'click';
				gridcolumns[columnviewmodel.Name].dismissOnEnter = false;

				if (columnviewmodel.Editable)
				{
					// Set canEdit to allow row specific editing
					gridcolumns[columnviewmodel.Name].canEdit = lang.hitch(this, this._canEditCell);

					switch(columnviewmodel.Type)
					{
						case "Aras.View.Columns.Boolean":
							gridcolumns[columnviewmodel.Name].editor = BooleanEditor;
							break;

						case "Aras.View.Columns.Date":
							gridcolumns[columnviewmodel.Name].editor = DateEditor;
							gridcolumns[columnviewmodel.Name].editorArgs = { style: 'width:' + (columnviewmodel.Width - 5) + 'px;' };
							break;

						case "Aras.View.Columns.Decimal":
							gridcolumns[columnviewmodel.Name].editor = DecimalEditor;
							break;

						case "Aras.View.Columns.Float":
							gridcolumns[columnviewmodel.Name].editor = FloatEditor;
							break;

						case "Aras.View.Columns.Integer":
							gridcolumns[columnviewmodel.Name].editor = IntegerEditor;
							break;

						case "Aras.View.Columns.Item":
							gridcolumns[columnviewmodel.Name].editor = ItemEditor;
							break;

						case "Aras.View.Columns.List":
							gridcolumns[columnviewmodel.Name].editor = ListEditor;
							break;

						case "Aras.View.Columns.String":
							gridcolumns[columnviewmodel.Name].editor = StringEditor;
							break;

						case "Aras.View.Columns.Text":
							gridcolumns[columnviewmodel.Name].editor = TextEditor;
							break;

						default:
							gridcolumns[columnviewmodel.Name].editor = undefined;
							break;
					}
				}
				else
				{
					gridcolumns[columnviewmodel.Name].editor = undefined;
				}

				// Cell Formatter
				gridcolumns[columnviewmodel.Name].formatter = lang.hitch(this, this._formatCell);

				// Set Column Width
				GridMisc.addCssRule('#' + GridMisc.escapeCssIdentifier(this._grid.domNode.id) +
						' .dgrid-column-' + GridMisc.escapeCssIdentifier(columnviewmodel.Name, '-'),
						'width: ' + columnviewmodel.Width + 'px;');

			}, this);

			// Set Grid Columns
			this._grid._setColumns(gridcolumns);

			// Update Rows
			this._updateRows();
		},

		_canEditCell(Grid, Cell) {

			if (Cell.ViewModel != null)
			{
				return Cell.ViewModel.Editable;
			}
			else
			{
				return false;
			}
		},

		_updateRows: function() {

			// Reset Rows
			array.forEach(this.Rows, function(row) {
				row.Visible = false;
			}, this);

			// Render Grid
			var rowdata = [];

			array.forEach(this.ViewModel.Rows, function(rowviewmodel, i) {

				if (this.Rows[i])
				{
					this.Rows[i].Visible = true;
				}
				else
				{
					this.Rows[i] = new Row({ Grid: this });
					this.Rows[i].startup();
				}

				this.Rows[i].ViewModel = rowviewmodel;
				this.Rows[i].ID = i;

				rowdata[i] = new Object();
				rowdata[i]['id'] = i;

				for (var j=0; j<this.ViewModel.Columns.length; j++)
				{
					if (!this.Rows[i].Cells[j])
					{
						this.Rows[i].Cells[j] = new Cell({ Row: this.Rows[i], Column: this.Columns[j]});
						this.Rows[i].Cells[j].startup();
					}

					this.Rows[i].Cells[j].set('ViewModel', rowviewmodel.Cells[j]);

					rowdata[i][this.ViewModel.Columns[j].Name] = this.Rows[i].Cells[j];
				}

			}, this);

			// Clear Grid
			this._grid.refresh();

			// Render Grid
			this._grid.renderArray(rowdata);
		},

		_formatCell: function(value, object) {

			switch(value.Column.ViewModel.Type)
			{
				case "Aras.View.Columns.Boolean":

					if (value.ViewModel.Value == '1')
					{
						return '<input type="checkbox" disabled checked="checked">';
					}
					else
					{
						return '<input type="checkbox" disabled>';
					}
				case "Aras.View.Columns.List":

					if (value.ViewModel.Selected != null)
					{
						return value.ViewModel.Selected.Label;
					}
					else
					{
						return '';
					}

				default:

					if (value.ViewModel.Value != null)
					{
						return value.ViewModel.Value;
					}
					else
					{
						return '';
					}
			}

		}

	});
});<|endoftext|>