Esempio n. 1
0
File: Button.js Progetto: drewlee/Lu
Button = Switch.extend( function( base ){
  var defaults = {
      on: 'click',
      /**
       * The time in milliseconds in which to throttle events.
       * Events will only be triggred once per throttle time.
       * This is useful when timing complex css transitions.
       * @property throttle
       * @type {Number}
       */
      throttle: 100,
      /**
       * By default the default is prevented setting this to true
       * allows the hash to be updated and urls to resolve
       * @property preventDefault
       * @type {Number}
       */
      preventDefault: true
    },
    root = 'lu/Button/decorators/',
    decorators = {
      first: root + 'first',
      last: root + 'last',
      load: root + 'load',
      next: root + 'next',
      pause: root + 'pause',
      play: root + 'play',
      previous: root + 'previous',
      select: root + 'select',
      state: root + 'state',
      def: root + 'default'
    };

  /**
   * Used for binding the space bar to the
   * Button's 'on' event as specified in the
   * configuration.
   * @method bindSpaceBar
   * @private
   */
  function bindSpaceBar( instance, on ){
    instance.$element.on( 'keyup', function( event ){
      if( event.keyCode === 32 ){
        instance.trigger( on );
      }
    } );
  }

  return {
    /**
     * Class constructor
     * @method initialize
     * @public
     * @param {Object} $element JQuery object for the element wrapped by the component
     * @param {Object} settings Configuration settings
     */
    init: function( $element, settings ){

      var self = this,
        action,
        requirements = [],
        decorator;

      _.defaults( settings, defaults );

      base.init.call( this, $element, settings );

      action = settings.action;

      if( action !== undefined ){
        switch( action ){
          case 'first':
            requirements.push( decorators.first );
            requirements.push( decorators.def );
            break;
          case 'last':
            requirements.push( decorators.last );
            requirements.push( decorators.def );
            break;
          case 'load':
            requirements.push( decorators.load );
            requirements.push( decorators.def );
            break;
          case 'next':
            requirements.push( decorators.next );
            requirements.push( decorators.def );
            break;
          case 'pause':
            requirements.push( decorators.pause );
            requirements.push( decorators.def );
            break;
          case 'play':
            requirements.push( decorators.play );
            requirements.push( decorators.def );
            break;
          case 'previous':
            requirements.push( decorators.previous );
            requirements.push( decorators.def );
            break;
          case 'select':
            requirements.push( decorators.select );
            break;
          case 'state':
            requirements.push( decorators.state );
            break;
          default:
            throw new Error( 'Button decorator "' + action + '" does not exist!' );
        }
      } else {
        requirements.push( decorators.def );
      }

      require.ensure( requirements, function( require, module, exports ){
        _.each( requirements, function( decorator, index ){
          decorator = require( decorator )( settings );
          Fiber.decorate( self, decorator );
          self.decorators.push(decorator);
        } );
        self.trigger( 'dependencies-resolved' );
      } );

      //binds the space-bar to the on event
      bindSpaceBar( this, settings.on );

      /**
       * Gets the url for the button -- either from the config setting or from the HREF
       * @method getUrl
       * @public
       * @return {String} The URL for the button
       */
       self.getUrl = function() {
        return settings.url || $element.attr('href');
      };

    },

    /**
     * Adds a disabled state to the Button
     * as well as adding the prop disabled if
     * it is a button or input element.
     * @method disable
     * @public
     * @return {Object} The Button instance
     */
    disable: function(){
      var $element = this.$element;
      this.addState( constants.states.DISABLED );
      if( $element.is( constants.HAS_A18_ATTRS ) ){
        $element.prop( constants.DISABLED, true );
      }
      return this;
    },

    /**
     * Removes the disabled state from the Button
     * as well the prop disabled if
     * it is a button or input element.
     * @method enable
     * @public
     * @return {Object} The Button instance
     */
    enable: function(){
      var $element = this.$element;
      this.removeState( constants.states.DISABLED );
      if( $element.is( constants.HAS_A18_ATTRS ) ){
        $element.prop( constants.DISABLED, false );
      }
      return this;
    }
  };
} );
Esempio n. 2
0
Container = Switch.extend( function ( base ) {
  /**
   * Default configuration values
   * @property defaults
   * @type Object
   * @private
   * @final
   */
  var defaults = {
    /**
     * The default state or states to be applied to the Container.
     * This can be an Array of strings or comma-delimited string
     * representing multiple states.
     * It can also be a string representing a single state
     * @property states
     * @type {String|Array}
     * @default null
     */
    states: null,
    content: null,
    /**
     * A URL to be used as a content source.
     * @property url
     * @type {String}
     * @default null
     */
    url: null,
    /**
     * A CSS selctor for an element to be used as a content source.
     * @property selector
     * @type {String}
     * @default null
     */
    selector: null,
    /**
     * Set to true if the content should be loaded in an iframe
     * @property frame
     * @type {Boolean}
     * @default false
     */
    frame: false,
    /**
     * When true the $element's height is set to the content height.
     * @property autoHeight
     * @type {Boolean}
     * @default false
     */
    autoHeight: false,
    /**
     * When true the $element's width is set to the content width.
     * @property autoWidth
     * @type {Boolean}
     * @default false
     */
    autoWidth: false,
    /**
     * A selector that specifies a target within the Container to inject content.
     * @property target
     * @type {String}
     * @default null
     */
    target: null
  };

  return {
    /**
     * Constructor
     * @method init
     * @public
     * @param {Object} $element JQuery object for the element wrapped by
     * the component
     * @param {Object} settings Configuration settings
     */
    init: function( $element, settings ){
      /**
       * Instance of Container
       * @property Container
       * @type Object
       * @private
       */
      var self = this,
        /**
         * The content of the container
         * @property content
         * @type String
         * @private
         */
        content,
        target;

      _.defaults( settings, defaults );

      base.init.call( self, $element, settings );

      /**
       * A cache to store the height and width of the $element
       * @property cache
       * @type Object
       * @public
       */
      self.cache = {};

      /**
       * A jquery object to inject content into
       * @property target
       * @type {Object}
       * @public
       */
      self.$target = null;

      target = settings.target;

      if( target ){
        self.$target = $element.find( target );
      } else {
        self.$target = $element;
      }

      /**
       * Loads content and then triggers an update event. Called on load event.
       * @method load
       * @private
       * @param {Object} $target Jquery object for the target node
       * @param {String|Object} source The source to load or obtain a URL from
       * @param {String} method the method to be used when inserting content
       * @return {Object} Container
       */
      function load( $target, source, method ){
        var isUrl = helpers.isUrl( source ),
          loadedContent,
          tmpData,
          url;

        if( !isUrl ){
          if (typeof source === "object" && source.getUrl) {
            url = source.getUrl();
          }
          else if (typeof source === "string") {
            url = source;
          }
          else if ( $target.is( 'a' ) ){
            url = $target.attr( 'href' );
          }

          // DO WE NEED THIS???
          if( !url && arguments.length > 1 ){
            method = url;
          }
        }

        if( url.indexOf( '#' ) === 0 ){
          loadedContent = $( url ).html();
          self.trigger( constants.events.UPDATE, [loadedContent, method] );
          return self;
        }

        if( settings.frame === true ){
          loadedContent = '<iframe src="' + url + '"></iframe>';
          self.trigger( constants.events.UPDATE, [loadedContent, method] );
          return self;
        }

        self.removeState( constants.states.LOADED );
        self.addState( constants.states.LOADING );

        $.ajax( {
          url: url,
          success: function( data, textStatus, jXHR ){
            var newContent,
              anchor = helpers.parseUri( url ).anchor;

            if( settings.selector ){
              newContent = $( data ).find( settings.selector ).html();
            } else if( anchor ){
              newContent = $( data ).find( '#' + anchor ).html() || data;
            } else {
              newContent = data;
            }

            self.removeState( constants.states.LOADING );
            self.addState( constants.states.LOADED );
            self.trigger( constants.events.UPDATE, [newContent, method] );
          },
          failure: function(){
           self.removeState( constants.states.LOADING ).addState( constants.states.ERRED );
          }
        } );

        return self;
      }

      /*
       * Updates content on an update event
       * @method update
       * @private
       * @param {Object} $target Jquery object for the target node
       * @param {String} updateContent The content to set
       * @param {String} method The method to use for setting the content.
       * This can specified as 'prepend' or 'append'. If theese are not
       * specified the content is replaced.
       * @return {Function} Container.setState
       */
      function update( $target, updateContent, method ){
        switch( method ){
          case 'append':
            self.appendContent( updateContent );
            break;
          case 'prepend':
            self.prependContent( updateContent );
            break;
          default:
            self.setContent( updateContent );
            break;
        }
      }

      /**
       * Returns the contents of the Container
       * @method getContent
       * @public
       * @return {Array} contents
       */
      self.getContent = function(){
        return content;
      };

      /**
       * Sets the content of the Container replacing current content
       * @method setContent
       * param {String} value The content to set.
       * @public
       * @return {Object} Container
       */
      self.setContent = function( value ){
        content = value;

        self.$target.html( content );

        if( settings.autoHeight ){
          delete this.cache.height;
          self.setHeight( this.getHeight() );
        }

        if( settings.autoWidth ){
          delete this.cache.width;
          self.setWidth( this.getWidth() );
        }

        self.trigger( constants.events.UPDATED, $element );
        return self;
      };

      /**
       * Appends content to the Container
       * @method appendContent
       * param {String} value The content to append.
       * @public
       * @return {Function} Container.setContent
       */
      self.appendContent = function( value ){
        self.setContent( content + value );
        return self;
      };

      /**
       * Prepend content to the Container
       * @method prepend Content
       * param {String} value The content to prepend.
       * @public
       * @return {Function} self.setContent
       */
      self.prependContent = function( value ){
        return self.setContent( value + content );
      };

      if( settings.url ){
        //Load content from url
        self.trigger( constants.events.LOAD );
      } else {
        //Store the $elements content
        content = $element.html();
      }

      //sets the height of the container automagically if autoHeight is set to true.
      if( settings.autoHeight ){
        this.setHeight( this.getHeight() );
      }

      //sets the width of the container automagically if autoHeight is set to true.
      if( settings.autoWidth ){
        self.setWidth( self.getWidth() );
      }

      // //Bind update event to update
      self.on( constants.events.UPDATE, function(event, content, method ) {
        event.stopPropagation();
        update( $( event.target ), content, method );
      });

      //Bind load event to load
      self.on( constants.events.LOAD, function(event, content, method ) {
        event.stopPropagation();
        //load( $(event.target), content, method );
        var things = [$(event.target)];
        if (content) {
          things.push(content);
        }
        if (method) {
          things.push(method);
        }
        load.apply(this, things);
      });
      
    },
    /**
     * Returns the computed height of the Container; result has no units
     * @method getHeight
     * @public
     * @return {Integer} Computed height of the Container (result drops units)
     */
    getHeight: function(){
      var height = this.cache.height,
          $target = this.$target;

      if( !height ){
        if( $target ){
          height = $target.height();
        } else {
          height = this.$element.height();
        }
        this.cache.height = height;
      }
      return height;
    },
    /**
     * Sets the height of the Container.
     * @method setHeight
     * @param {Integer} value The height in pixels to set
     * @public
     * @return {Object} Container
     */
    setHeight: function( value ){
      var $target = this.$target;

      this.cache.height = value;
      if( $target ){
        $target.height( value );
      } else {
        this.$element.height( value );
      }
      return this;
    },
    /**
     * Returns the computed width of the Container; result has no units
     * @method getHeight
     * @public
     * @return {Integer} Computed height of the Container (result drops units)
     */
    getWidth: function(){
      var width = this.cache.width,
          $target = this.$target;

      if( !width ) {
        if( $target ){
          width = $target.width();
        } else {
          width = this.$element.width();
        }
        this.cache.width = width;
      }
      return width;
    },
    /**
     * Sets the width of the Container
     * @method setWidth
     * @param {Integer} value The width in pixels to set
     * @public
     * @return {Object} Container
     */
    setWidth: function( value ){
      var $target = this.$target;

      this.cache.width = value;
      if( $target ){
        $target.width( value );
      } else {
        this.$element.width( value );
      }
      return this;
    }
  };
} );
Esempio n. 3
0
File: List.js Progetto: Droppe/Lu
List = Switch.extend( function( base ){

  var SELECTED = constants.statePrefix + constants.states.SELECTED,
    LIST_TAGS = 'ul, ol, dl',

    defaults = {
      index: undefined
    },

    root = 'lu/List/decorators/',

    decorators = {
      viewport: root + 'viewport'
    };

  return {
    /**
     * @constructor
     * @method init
     * @public
     * @param {Object} $element JQuery object for the element wrapped by the component
     * @param {Object} settings Configuration settings
     */
    init: function( $element, settings ){
      var self = this,
        Selected,
        Previous,
        $items,
        index;

      _.defaults( settings, defaults );

      base.init.call( this, $element, settings );

      var requirements = [];
      if (settings.viewport) {
        requirements.push(decorators.viewport);
      }

      require.ensure( requirements, function( require, module, exports ){
        _.each( requirements, function( decorator, index ){
          decorator = require( decorator )( settings );
          Fiber.decorate( self, decorator );
        } );
        self.trigger( 'dependencies-resolved' );
      } );

      /**
       * gets the 0 based index of the selected item.
       * @method index
       * @public
       * @return {Number} index
       */
      this.index = function(){
        return index;
      };

      /**
       * Gets the selectable items
       * @public
       * @return {Object} a JQuery object-reference to the items
       */
      this.items = function(){
        var $items;

        if( settings.items ){
          if( typeof settings.items === 'string' ){
            $items = $element.find( settings.items );
          } else {
            $items = settings.items;
          }
        } else {
          if( $element.is( LIST_TAGS ) ){
            $items = $element.children();
          } else {
            $items = $element.children( LIST_TAGS ).first().children();
          }
        }

        if( !$items ){
          $items = $element.children();
        }

        return $items;
      };

      /**
       * Returns the currently-selected Container.
       * @method current
       * @public
       * @return {Object} JQuery object-reference to the selected item
       */
      this.current = function(){
        return Selected;
      };

      /**
       * Select an item in the list
       * @method select
       * @public
       * @param {Integer|String|Object} item The index of the item to
       * select, a css selector, or a JQuery collection containing the item.
       * @return {Object} self
       */
      this.select = function( item ){
        var component,
          componentData,
          $item,
          idx;

        //return if no item was passed in.
        if( item === undefined ){
          return this;
        }

        //try to determine the index of the item being selected
        idx = ( typeof item === 'number' ) ? item : undefined;

        //Figure out what to select based on the param passed in.
        if( typeof item === 'number' && item <= this.size() - 1 ){
          $item = this.$items.eq( item );
        } else if( typeof item === 'string' ){
          $item = this.$items.filter( item );
          $item = ( $item.size() === 1 ) ? $item : undefined;
        } else if( item instanceof $ && item.size() === 1 ){
          if( item.is( this.$items ) ){
            $item = item;
          }
        }

        //We could not determine which item to select so...
        if( $item === undefined ){
          this.trigger( constants.events.OUT_OF_BOUNDS, [this] );
          return this;
        }

        //Get the index of the item to be selected if we don't have it from above
        if( idx === undefined ) {
          idx = this.$items.index( $item );
        }

        if( idx > this.index() ){
          this.addState( constants.states.FORWARD ).removeState( constants.states.REVERSE );
        } else if( idx < this.index() ){
          this.addState( constants.states.REVERSE ).removeState( constants.states.FORWARD );
        }

        _.each( $item.lu( 'getComponents' ), function( comp, key ){
          var instance;
          if( !component ){
            instance = comp.instance;
            if( instance ){
              if( typeof instance.removeState === 'function' && typeof instance.addState === 'function' ){
                component = comp;
              }
            }
          }
        } );

        //an acceptable component was not found.
        if( !component ){
          Lu.map( $item, 'Switch', function(){} );
          Lu.execute( $item );
          component = $item.lu( 'getComponents' ).Switch;
        }


        //Once the item is fully instantiated, select it.
        component.deferral.then( function( Switch ){
          var current = self.current();

          //If there is a currently selected item remove the selected state
          if( current ){
            current.removeState( constants.states.SELECTED );
          } else {
            self.$items.filter( '.' + SELECTED ).not( Switch.$element ).removeClass( SELECTED );
          }

          Selected = Switch;
          index = idx;
          Selected.addState( constants.states.SELECTED );
          self.trigger( constants.events.SELECTED, [self] );
        } );

        return this;
      };

      this.$items = this.items();

      index = settings.index;
      if( index === undefined ){
        var $selected = this.$items.filter('.' + SELECTED );
        index = this.$items.index( $selected );
        if( index === -1 ){
          index = 0;
        }
      }

      //Automatically select an item during init
      self.select( index );

      this.on( constants.events.SELECT, function( event, component ){
        event.stopPropagation();

        var $element = component.$element,
          controls = $element.attr( 'aria-controls' ),
          href,
          $item;

        if( !controls ){
          href = $element.attr( 'href' );
          if( href ){
            controls = helpers.parseUri( href ).anchor;
          }
        }

        if( controls ){
          $item = $( '#' + controls );
          if( $item.is( self.$items ) ){
            self.select( $item );
          } else {
            self.select( component.$element.closest( self.$items ) );
          }
        } else {
          self.select( component.$element.closest( self.$items ) );
        }
      } );

      this.on( constants.events.NEXT, function( event ){
        event.stopPropagation();
        self.next();
      } );

      this.on( constants.events.PREVIOUS, function( event ){
        event.stopPropagation();
        self.previous();
      } );

      this.on( constants.events.FIRST, function( event ){
        event.stopPropagation();
        self.first();
      } );

      this.on( constants.events.LAST, function( event ){
        event.stopPropagation();
        self.last();
      } );

      this.on( constants.events.STATED, function( event, component ){
        event.stopPropagation();
        var current;

        if( !component.$element.is( self.$items ) ){
          return;
        }

        current = self.current();

        if( current ){
          if( component.$element.is( current.$element ) ){
            return;
          }
        }

        if( component.hasState ){
          if( component.hasState( constants.states.SELECTED ) ){
            self.select( component.$element );
          }
        }
      } );

    },

    /**
     * adds a new item to $element
     * @method append
     * @public
     * @param {Array} A jQuery Collection of $items to append
     * @return {Object} self
     */
    add: function( $item ){
      this.$items.parent().append( $item );
      this.$items = this.items();
      return this;
    },

    /**
     * Removes an item from $element
     * @method remove
     * @public
     * @param {Array} A jQuery Collection of $items to remove
     * @return {Object} self
     */
    remove: function( $item ){
      $( $( $item ), this.$items ).remove();
      this.$items = this.items();
      return this;
    },
    /**
     * Selects the next item in the list.
     * @method next
     * @public
     * @return {Object} self
     */
    next: function(){
      if( this.hasNext() ){
        this.select( this.index() + 1 );
      } else {
        this.trigger( constants.events.OUT_OF_BOUNDS, [this] );
      }
      return this;
    },
    /**
     * Selects the previous item in the list.
     * @method previous
     * @public
     * @return {Object} self
     */
    previous: function(){
      if( this.hasPrevious() ){
        this.select( this.index() - 1 );
      } else {
        this.trigger( constants.events.OUT_OF_BOUNDS, [this] );
      }
      return this;
    },
    /**
     * Selects the last item in the list.
     * @method last
     * @public
     * @return {Object} self
     */
    last: function(){
      this.select( this.$items.eq( this.size() - 1 ) );
      return this;
    },
    /**
     * Selects the first item in the list.
     * @method first
     * @public
     * @return {Object} self
     */
    first: function(){
      this.select( 0 );
      return this;
    },
    /**
     * Determines if there are any higher-index items in the list.
     * @method hasNext
     * @public
     * @return {Boolean} true if not at the last item in the list
     */
    hasNext: function(){
      return ( this.index() < this.size() - 1 );
    },
    /**
     * Determines if there are any lower-index items in the list.
     * @method hasPrevious
     * @public
     * @return {Boolean} true if not at the first item in the list
     */
    hasPrevious: function(){
      return ( this.index() > 0 );
    },
    /**
     * Returns the number of items in the list.
     * @method size
     * @public
     * @return {Number} The number of items in the list
     */
    size: function(){
      return this.$items.size();
    }
  };
} );