d4tocchini
9/15/2011 - 2:33 AM

G.coffee

G.coffee

###

  One grid system to rule them all, and in the javascript bind them
  
  
  Concerns
  =====================================
  
    - IE positioning... see isotope.js and https://github.com/louisremi/jquery.transform.js#
      - needs to be absolue in relative? or absolute in absolute too?
      - http://stackoverflow.com/questions/3667549/absolute-positioned-div-within-another-absolute-positioned-div-does-not-show-on-i
  
  
  RESOURCES
  ======================================
    
    jQuery Template Layout Spec
      - http://a.deveria.com/csstpl/
    
    Marcus Gärde's Way of Typography
      - http://www.bachgarde.com/html/works/gridsystem.html
      - In fact, the baselinegrid always fitted perfectly on the page. And even the gutter was in proportion to the lead.
      - [InDesign Pro Grid Calc for $100](https://www.designersbookshop.com/grid-calculator-pro-edition.html)
    
    Vignelli's UniGrid Sytem
      - http://www.aisleone.net/2010/design/massimo-vignellis-unigrid-system/
    
    DIN paper system
      - http://en.wikipedia.org/wiki/Paper_size#The_international_standard:_ISO_216
    
    The Golden Grid System
      - A folding grid for responsive design.
      - http://goldengridsystem.com/
    
    Russian Modular Grid
      - http://thegrids.ru/
      - PS: http://modulargrid.org/#app
      - Algorithms: http://cherenkevich.livejournal.com/38353.html, http://cherenkevich.livejournal.com/38454.html, http://cherenkevich.livejournal.com/38839.html
  
    Fluid Grids
      - http://www.alistapart.com/articles/fluidgrids/
      
    Inpsiration
      - http://www.aisleone.net/
  
  THEORY
  ======================================
    
    Math
    -------------------------------
    
    A grid is arbitrary division of an available space in a plane into discrete units.  
    Each set of discrete units is a grid dimension.
    Two, non parrell grid dimensions consitute a finite vector space.
    Layouts of print and web content are comprised of sets of planes fit into this vector space.
    Each pie
    
    
    CSS Layouts
    -----------------------------
    http://www.alistapart.com/articles/css-floats-101/
      
      According to the W3C:
      A float is a box that is shifted to the left or right on the current line. The most interesting characteristic of a float (or “floated” or “floating” box) is that content may flow along its side (or be prohibited from doing so by the “clear” property). Content flows down the right side of a left-floated box and down the left side of a right-floated box.
      
      [normal flow](http://www.w3.org/TR/CSS21/visuren.html#normal-flow)
    
    http://www.alistapart.com/articles/css-positioning-101/
    
    http://www.positioniseverything.net/easyclearing.html
    
    http://mattwilcox.net/archive/entry/id/1030
    http://mattwilcox.net/archive/entry/id/1037/
      The W3C, still, are not willing to believe or are somehow incapable of understanding that the abilities of CSS today are inadequate for the job it must now perform compared to the job it was created to handle. And they are unwilling to re-think the very fundamentals of CSS that are crippling designers today. It’s almost a decade since CSS could be said to have “gone mainstream” - and yet designers are still waiting for a way to make things line up horizontally that doesn’t involve butchering the mark-up. Are still waiting for layout tools that allow us to do some really fundamental basics. Are still waiting for layout options flexible enough for us to use without resorting to complex hacks. And the future looks as bad as the present if the god-forsaken horror that is the “Advanced” layout module of CSS3 is any indication.
      
    (CSS needs a bit of basic love)[http://mattwilcox.net/archive/entry/id/1059/]
      You can’t position relative to a given element, only to a positioned parent element. Making it impossible to truely seperate mark-up structure from display layout.
      We’re still lacking constants, variables, and math. Bert Bos can argue they’re not needed until he’s blue in the face, but the fact remains he’s wrong. There are reasons that SASS and LESS were developed and are being used by designers not just developers. But as nice as they are, they’re just battle-field medicine and we need a real solution.
    
    Masonry
    ----------------------------
    Masonry is a dynamic grid layout plugin for jQuery. Think of it as the flip-side of CSS floats. Whereas floating arranges elements horizontally then vertically, Masonry arranges elements vertically, positioning each element in the next open spot in the grid. The result minimizes vertical gaps between elements of varying height, just like a mason fitting stones in a wall.
    
    CSS Is Not Suited for Layout!
    -----------------------------
      - [Call For a Layout System](http://meyerweb.com/eric/thoughts/2009/02/17/wanted-layout-system/)      
        - But my issue is that they are trying to define layout systems without understanding what designers actually want to do, and how that maps to best-practice HTML authoring.
          Give me math, variables, and constants. Give me positioning relative to a specified element. With those things I can make my OWN layout solution. Implement a grid system, and all independent of mark-up order. None of the proposed layout solutions address the basic problems we have.
      - [The fundamental problems with CSS3](http://mattwilcox.net/archive/entry/id/1031/)
      - http://www.flownet.com/ron/css-rant.html
  
    http://www.w3.org/TR/css3-grid-layout/#basic-capabilities  
  
  
    fluid or static
  
    source order independence
    ------------------------
  
      - http://www.sitepoint.com/should-html-dom-order-match-visual-layout/
    
      - http://www.sitepoint.com/give-floats-the-flick-in-css-layouts/
    


###


$ = jQuery
# Wrap in Closure to avoid global variables.
$ ->
  
  root.G = {}
  

  
  #8888888ba,    88                                                          88                            
  #8      `"8b   ""                                                          ""                            
  #8        `8b                                                                                            
  #8         88  88  88,dPYba,,adPYba,    ,adPPYba,  8b,dPPYba,   ,adPPYba,  88   ,adPPYba,   8b,dPPYba,   
  #8         88  88  88P'   "88"    "8a  a8P_____88  88P'   `"8a  I8[    ""  88  a8"     "8a  88P'   `"8a  
  #8         8P  88  88      88      88  8PP"""""""  88       88   `"Y8ba,   88  8b       d8  88       88  
  #8      .a8P   88  88      88      88  "8b,   ,aa  88       88  aa    ]8I  88  "8a,   ,a8"  88       88  
  #8888888Y"'    88  88      88      88   `"Ybbd8"'  88       88  `"YbbdP"'  88   `"YbbdP"'   88       88
  
  ###\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
    TODO:
      - normalize angle values
  
  /////////////////////////////////////////////////////////////////////////////////////////////////////###
  class Dimension extends RW.Model
    
    is_horizontal: false
    is_vertical: false
    is_angled: false
    
    defaults:
      id:       null
      # angle = the perpendicular of the grid lines from
      angle:    'horizontal'  #  'vertical'  ||  [NUMBER, 0-360], 0 = 'horizontal', 90 = 'vertical'
      _angle:   0
      align:    'middle'      #  'start'  ||  'end'
      _zoneUnit: null
      unit:     null
      _unit:    null
      count:    null
      _count:   null
      countMax: null
      gutter:   0
      _gutter:  null
      lines:    null
      angle:    null
      lines:    null
      size:     0
    
    defaultLines:
      golden:     '38.1966011%' # 1 - (phi^(-1)) = 0.381966011
      golden2:    '61.803399%'  # 1/phi = .61  
      squareOf2:  '70.7106781%' # sqrt(2)^(-1) = 0.707106781
    
    # This method is attached to the the it's grid like so:
    #   G[grid_id][dimension_id.self]()
    # Basically, just some API sugar to help drilling down Grid properties
    self: (num, isZone = true) ->
      # return this dimension if num is not defined
      if !num
        return this
      # else, return dimension measurements if num is passed
      if isZone
        return @zoneUnit(num)
      else
        return @unit(num)
      
    initialize: () ->
      _.bindAll this, 'self'
      that = this
      if !this.id then ERROR 'Grid Dimension requires an id'
      
      count = this.get 'count'
      unit  = this.get 'unit'
      if !(unit or count) then ERROR 'Grid Dimension needs either unit or count'
      if (unit and count) then ERROR 'Grid Dimension cant have both unit and count set'
      
      el = this.get 'el'
      if !el? then ERROR 'Grid Dimension needs an el to measure itself from'
      @el = el
      $el = $(el)
      
      _.bindAll this, 'onSizeChange'
      @bind 'change:size', @onSizeChange
      # determine how to get the available size for this dimension
      # by using the angle of the dimension
      angle = @get 'angle'
      
      if angle is 'horizontal' 
        @is_horizontal = true
        @set {_angle: 0}, {silent: true}
        this.measure_size = () ->
          return $el.width()
          
      else if angle is 'vertical'
        @is_vertical = true
        @set {_angle: 90}, {silent: true}
        this.measure_size = () ->
          return $el.height()
          
      else if _.isNumer angle 
        this.measure_size = () ->
          sin = Math.sin(angle)
          if sin > 0
            return $el.height() / sin               # r = y / sin(angle)
          else
            return $el.width() / Math.cos(angle)    # r = x / cos(angle)
      
    # sets the dimension size, if its a new dimension the set method dispactches 'change:size'
    measure: () ->
      this.set
        'size': this.measure_size()
    
    onSizeChange: () ->      
      if @get('unit')? and !@get('count')?
        @measureWithFixedUnit()
      else 
        @measureWithFixedCount()
    
    size: (dimUnits = 'px') ->
      return this.get 'size'
    
    count: () ->
      return @get '_count'
    
    countRemainder: () ->
      return @get '_countRemainder'
      
    remainder: () ->
      return @get '_remainder'
    
    unit: (num = 1) ->
      return num * @get('_unit') + ((num - 1) * @gutter())

    zoneUnit: (num = 1) ->
      return num * @get '_zoneUnit'
    
    gutter: () ->
      return @get '_gutter'     
    
    # helper for templates
    gutterHalf: () ->
      return @gutter() / 2    
         
    # Rounding
    # - Be careful rounding percentages!!!  if the grid hasn't been measured yet, then the grid has to go through another measure to work, which sucks
    floor: (value) ->
      return @round value, 'floor'    
    ceil: (value) ->
      return @round value, 'ceil'
    round: (value, roundMethod = 'floor') ->
      value     = @normalizeToPxValue value 
      size      = @size()
      zoneUnit  = @zoneUnit()
      #if value > size then value = size
      numZoneUnits = value / zoneUnit
      if numZoneUnits < 1 
        value = zoneUnit
      else
        value = zoneUnit * Math[roundMethod] numZoneUnits
      return value   
    
    measureWithFixedUnit  : () ->
      unit                = @normalizeToPxValue @get 'unit'
      gutter              = @normalizeToPxValue @get 'gutter'
      size                = @size() 
      zoneUnit            = unit + gutter                                    
      countFrac           = size / zoneUnit           
      count               = Math.floor countFrac
      countRemainder      = count - countFrac
      remainder           = countRemainder * zoneUnit
      @set
        '_unit'           : unit
        '_zoneUnit'       : zoneUnit
        '_count'          : count
        '_countRemainder' : countRemainder
        '_remainder'      : remainder
        '_gutter'         : gutter
    
    measureWithFixedCount : () ->
      count               = @get('count')
      gutter              = @normalizeToPxValue @get 'gutter'      
      size                = @size()   
      countRemainder      = 0
      remainder           = 0                                                                                        
      zoneUnit            = ( size / count )
      unit                = zoneUnit - gutter 
      @set
        '_unit'           : unit
        '_zoneUnit'       : zoneUnit
        '_count'          : count
        '_countRemainder' : countRemainder
        '_remainder'      : remainder
        '_gutter'         : gutter
    
    normalizeToPxValue: (value, sizeContext) ->
      sizeContext or sizeContext = @size()
      return _.toPxValueRelativeTo value, sizeContext
    
    previewItemCss: () ->
      gutterHalf = @gutterHalf()
      angle = @get 'angle'
      if angle is 'horizontal' 
         return 'width: ' + @unit() + 'px; height:100%; margin: 0 ' + gutterHalf + 'px;'
      else if angle is 'vertical'
          return 'height: ' + @unit() + 'px; width:100%; margin: ' + gutterHalf + 'px 0;'    
          
    previewItems: () ->
      items = []
      count = @count()
      dimension = this
      _.each _.range(count), (i) ->
        items[i] = dimension
      return items
    
    previewZones: () ->
      
      
    previewZone: () ->
      angle = @get 'angle'
      if angle is 'horizontal'        
        model = 
          width: @zoneUnit()
          height: '100%'
      else if angle is 'vertical'
        model = 
          height: @zoneUnit()
          width: '100%'
                
    
    nodeSpan: (num) ->
      return @unit()
    
    
    
    sizeOf: (num, isContainer) ->
      
      return 
    
  class DimensionCollection extends RW.Collection
    model: Dimension    
  
  
  ###888888888                                        
           ,88                                        
         ,88"                                         
       ,88"      ,adPPYba,   8b,dPPYba,    ,adPPYba,  
     ,88"       a8"     "8a  88P'   `"8a  a8P_____88  
   ,88"         8b       d8  88       88  8PP"""""""  
  88"           "8a,   ,a8"  88       88  "8b,   ,aa  
  888888888888   `"YbbdP"'   88       88   `"Ybb###
  
  ###
  
  TODO:
    - Order
    - suffix, prefix...
    - Spawn Methods
    - layouts
    - orphaned?
  ###
  class Zone extends RW.Model    
    className: () ->
      return 'g_zone_' + @cid
    
    itemClassName: () ->
      return 'g_zone_' + @cid + '_item'
          
    #items: null
    defaults:
      id            :   null                
      is_active     :   false      
      htmlTag       :   'div'
      clear         :   false  # doesnt do anything yet
      generator     :   null
      # size
      size          :   null                
      width         :   'auto'  
      _width        :   'auto'       
      height        :   'auto'
      _height       :   'auto'                         
      # min / max size
      min_width     :   0
      max_width     :   'none'
      min_height    :   0
      max_height    :   'none' 
      _min_width    :   0
      _max_width    :   'none'
      _min_height   :   0
      _max_height   :   'none'
      # hierarchy
      parentId      :   null  
      spawner    :   null
      childDefaults :   null
      # z-index
      #z             :   0
      # layout
      float         :   'left'      #  ''
      _css_position :   'relative'  #  'absolute'
      childSelector :   null
      # position
      top           :   'auto'
      left          :   'auto'            
      bottom        :   'auto'
      right         :   'auto'       
      _top          :   'auto'
      _left         :   'auto'            
      _bottom       :   'auto'
      _right        :   'auto'     
      # t r l b gutters
      _bGutter      :   0
      _lGutter      :   0
      _rGutter      :   0
      _tGutter      :   0
      lGutter       :   () ->
        return this.grid.dimensions.models[0].gutter() / 2      
      rGutter       :   () ->
        return this.grid.dimensions.models[0].gutter() / 2      
      tGutter       :   () ->
        return this.grid.dimensions.models[1].gutter() / 2      
      bGutter       :   () ->
        #if @get('height') is 'auto' 
        #  return 'auto'
        return this.grid.dimensions.models[1].gutter() / 2
      
      
    initialize: () ->
      @grid = @get 'grid'
      if !@grid? then ERROR 'Zone needs a Grid!'      
      ###
      childZones = this.get 'zones'
      if childZones?
        this.addZones childZones
      ###
    
    render: () ->
      this.el = $.templates.G_zone_template( this ) #$.templates.G_zone_template(zone).width(z_w).height(z_h).css({float:'left'}).appendTo($el)
      return this.el
    
      
    place: (itemEl, guttered = true) ->
      $item = $(itemEl)
      attrName = 'data-placed'
      $item.attr attrName, @id
      # place item in zone
      if guttered
        $item.addClass(@itemClassName()).addClass('guttered').appendTo(this.el)
        #$item.addClass(@itemClassName()).addClass('guttered').position
        #  my: 'top left'
        #  at: 'top left'
        #  of: this.el
        
      else
        $item.addClass(@itemClassName()).appendTo(this.el)
      # TODO, make better
      #@items.push $item
    

    measurables: ['width', 'height', 'lGutter', 'rGutter', 'tGutter', 'bGutter', 'min_width', 'max_width', 'min_height', 'max_height', 'top', 'right', 'bottom', 'left']

    measure: () ->
      that = this

      # get an object with the internal version of measurable attributes that are CSS safe.
      setObj = @getInternalMeasurements()
      # Before setting the internal measurements, let's check them for dependencies that may change other attributes.
      setObj = @validateInternalMeasurements(setObj)      
      # set the internal measurements, which dispatches a change event, and thus will update the zone's CSS
      @set setObj    

    populate: () ->      
      attrOld = 'data-g-position'
      
      # D4 TODO: better context handling?
      context = $('html') #@grid.bufferEl
      
      # place els into generated zones
      elsToPlaceSelector = '[' + attrOld + '=' + this.id + ']'            
      elsToPlace = context.find( elsToPlaceSelector )     
      elsToPlace.removeAttr attrOld
      # D4 TODO: better context handling?
      for el in elsToPlace
        @place(el)
    
    
    layout: () ->       
      # set children width and height and stuff
    
    setup: () ->
      @setupGenerator()
      
    setupGenerator: () ->
      generatorSettings = @get 'generator'  
        
      # do nothing if generator isnt defined   
      if !generatorSettings then return false
        
      # if first time, instaniate @generator
      if !@generator?
        generatorSettings.zone  =     this
        @generator              = new ZoneGenerator(generatorSettings)       
      # else, update @generator 
      else
        @generator.set generatorSettings      
      #
      @generator.setup()
      @generator.autoGenerate()
      
    
    # Sets parentId of each Zone Model to this zone
    # Then, adds the Zone Models via this zone's grid
    addZones: (models) ->
      that = this
      if _.isArray models 
        _.each models, (model) ->
          model.parentId = that.id 
      else
        models.parentId = that.id 
      this.grid.addZones(models)        
    
    children: () ->
      @grid.zones.getChildrenOf this
    
    # !!!!!!!!!!!!!!!
    childrenWidth: () ->
      return $(@el).childrenWidth()
    
    # !!!!!!!!!!!!!!!
    childrenHeight: () ->
      return $(@el).childrenHeight()
      ###
      currentHeight = 0
      _.each @children(), (child) ->
        # only measure height of children zones whose height is not dependent on parent
        if !_.includes child.get('height'), '%'          
          $child = $(child.el)
          h = $child.position().top + $child.height()
          if h > currentHeight then currentHeight = h
      ###
    dynamicallySize: () ->
      # set the zone height to height of the children
      if @get('height') is 'auto'
        $(@el).height(@childrenHeight())
        
      if @get('width') is 'auto'
        $(@el).width(@childrenWidth())
    
    #finalizeSize
    
    getInternalMeasurements: () ->
      obj = {}
      that = this
      _.each @measurables, (attr) ->
        internalAttr = '_' + attr
        internal = that.toCssValue that.get(attr)
        if internal then obj[internalAttr] = internal
      return obj    
      
    validateInternalMeasurements: (obj) ->
      obj or obj = {}
      
      # validate position props
      # if any of the t r b l position props are set, then make sure that the css position = 'absolute'    
      positionKeys = ['_top', '_right', '_bottom', '_left']
      is_positioned = false
      for posKey in positionKeys
        if obj[posKey]? and obj[posKey] isnt 'auto'
          is_positioned = true
          break
      if is_positioned
        obj['_css_position'] = 'absolute'
      else
        obj['_css_position'] = 'relative'
        
      # validate width / height stuff
      #if obj._width is 'auto'
        
      
      # TODO: validate float stuff...
      return obj
    
            
    toCssValue: (value) ->
      if !value then return null    
      # call functions, if function returns number it will add 'px' suffix
      if _.isFunction value then value = value.call this
      # calculate grid calculations
      if _.startsWith value, 'G.'
        value = eval(value)
      # ensure value is CSS safe, suffix with 'px' if number
      if _.isNumber( value ) then value = value + 'px'
      return value
      
    
    # not used in internal measure flow 
    setInternalAttr: (attr) ->
      setObj = {}
      internalAttr = '_' + attr
      internal = @toCssValue @get(attr)
      if internal then setObj[internalAttr] = internal
      @set setObj
                     

    ###
    activate: () ->
      @set
        is_active: true

    deactivate: () ->  
      @set
        is_active: false  
    ###

  $.tags.add
    name: 'G_zone_template_styles'
    template:
      '
        <style {{isTheTag}}>
          .{{className}} { 
            float:{{float}}; 
            position:{{_css_position}};
            left:{{_left}}; right:{{_right}}; top:{{_top}}; bottom:{{_bottom}};
            width:{{_width}}; height:{{_height}}; min-width:{{_min_width}}; min-height:{{_min_height}}; max-width:{{_max_width}}; max-height:{{_max_height}};
          }
          .{{className}} .{{itemClassName}} {
            left:0px; right:0px; top:0px; bottom:0px; position:absolute !important;
          }
          .{{className}} .guttered {
            left:{{_lGutter}}; right:{{_rGutter}}; top:{{_tGutter}}; bottom:{{_bGutter}};
          }
        </style>
      '
  $.tags.add
    name: 'G_zone_template'
    api:      
      color: () ->
        return null #'hsla('+_.randomIntRange(0,360)+',100%,'+_.randomIntRange(0,100)+'%,.2)'
    template:
      '
        <{{htmlTag}} {{isTheTag}} class="{{className}}" data-g-zone="{{id}}" style="background-color:{{color}};">
          {{>G_zone_template_styles}}
        </{{htmlTag}}>
      '
  
  class ZoneCollection extends RW.Collection
    model: Zone
  
  

  ###888888888                                          ,ad8888ba,                                                                                                  
           ,88                                         d8"'    `"8b                                                                 ,d                              
         ,88"                                         d8'                                                                           88                              
       ,88"      ,adPPYba,   8b,dPPYba,    ,adPPYba,  88              ,adPPYba,  8b,dPPYba,    ,adPPYba,  8b,dPPYba,  ,adPPYYba,  MM88MMM   ,adPPYba,   8b,dPPYba,  
     ,88"       a8"     "8a  88P'   `"8a  a8P_____88  88      88888  a8P_____88  88P'   `"8a  a8P_____88  88P'   "Y8  ""     `Y8    88     a8"     "8a  88P'   "Y8  
   ,88"         8b       d8  88       88  8PP"""""""  Y8,        88  8PP"""""""  88       88  8PP"""""""  88          ,adPPPPP88    88     8b       d8  88          
  88"           "8a,   ,a8"  88       88  "8b,   ,aa   Y8a.    .a88  "8b,   ,aa  88       88  "8b,   ,aa  88          88,    ,88    88,    "8a,   ,a8"  88          
  888888888888   `"YbbdP"'   88       88   `"Ybbd8"'    `"Y88888P"    `"Ybbd8"'  88       88   `"Ybbd8"'  88          `"8bbdP"Y8    "Y888   `"YbbdP"'   88          

    - The ability to dynamically generate zones in the grid was an important feature for me.
    - Organicaly random layouts with ordered severity
    - Flipboard, Grid Masters
    - akin to a particle generator
    
    Zones can be generated by:
      - Defining 'ZoneSeeds'
      - Programaticaly via interpolation of ZoneGenerator Parameters
    
    ZoneSeeds
      - A ZoneSeed generates a Zone with specified attributes
      - The most common use case is, given a Collection of ZoneSeeds, to generate Zones from a set of DOM Elements
    
    Generating Zones programatiicaly
      - 
    
    Demos
      - Navigation Menus
      - Flipboard Layout
      - Infinite Scrolling
      
    RAQ (rarely asked questions)
      - Global ZoneGenerator or ZoneFactory?
        - Debated it, but each zone needs to maintain state of last rendered seed, so it made sense to init a Generator on each Zone
   
  ////////////////////////////////////////////////////////////////////////////////////////////////////////////###
  
  class ZoneSeed extends RW.Model
    defaults:
      clear: false      
    
  class ZoneSeedCollection extends RW.Collection    
    model: ZoneSeed
  
  class ZoneGenerator extends RW.Model        
    
    defaults:
      el: null # or el
      clear: false
      shuffle: false
      seeds: null          
      is_infinite: false
      infinite_direction: 'vertical'
      infinite_buffer: '200px'
      loadingGif: null
      templateId: 'wall_tiles'
    
          
    initialize: () ->
      @zone = @get 'zone' 
      if !@zone? then ERROR 'ZoneGenerator requires a zone'      
      @grid = @zone.grid
      @id = @zone.id
      
      # create this.seeds collection
      @seeds = new ZoneSeedCollection()
    
    setup: () ->      
      @setupSeeds()
      
    setupSeeds: () ->
      _.attrToSelf_collection_proxy(this, 'seeds').refresh()

    addSeeds: (models) ->
      models = _.flattenModels(models)
      #@prepSeeds(models)
      @seeds.add( models )
      
    autoGenerate: () ->
      bufferEl = @grid.bufferEl
      emitAttr = 'data-g-emit'
      # populate els that need their own zone emitted from the zone generator
      elsToEmitSelector = '[' + emitAttr + '=' + @zone.id + ']'
      elsToEmit = bufferEl.find( elsToEmitSelector )            
      elsToEmit.removeAttr emitAttr
      #@addToLayout(elsToEmit) 

      if elsToEmit?.length?  # D4 hack
        if elsToEmit.length > 0      
          @generate {els: elsToEmit}
              
    count: 0
    seeds_i: 0
    prev_seeds_i: 0
    seeds_num: () ->
      return @seeds.models.length
      
    generateDefaults:
      els: null
      num: 'auto'
      buffer: '200px'      
      
    generate: (options) ->
      
      options or options = {}
      that = this
      # use the el to make child zones
      # populate child zones with the el
      parentZone = @zone
      parentId = @zone.id
      
      seeds = @seeds
      seeds_num = @seeds_num()
      nextSeed = () ->
        seeds_i = that.seeds_i
        if seeds_i > seeds_num - 1 then seeds_i = 0        
        seed = seeds.at seeds_i 
        seeds_i = seeds_i + 1  
        that.seeds_i = seeds_i
        return seed
        
      els = options.els
      if els?
        _.each els, (el) ->     
          $el = $(el)
          
          # find a suitable spawn, maybe creat a new zone
          seed_found = false
          while !seed_found
            childZone = {}
            seed = nextSeed()
            childZone.id = parentZone.id + '_generated_' + that.count
            childZone = _.defaults childZone, seed.attributes
            # add the zone if clear
            if seed.get 'clear'
              that._addChildZone( childZone )
            else
              seed_found = true
            #else if !childZone.if?
            #  spawn_found = childZone.if.call that, $el

          #!!!!! makes the el ready to be positioned into its zone
          $(el).attr('data-g-position', childZone.id)
          that._addChildZone( childZone )
      
    #_getChildZoneId: () ->
    #  return  @zone.id + '_generated_' + @count
    
    _addChildZone: (childZone) ->
      @count = @count + 1    
      @zone.addZones childZone
      
      
         #b                                             
        #88b                                            
       #8'`8b                                           
      #8'  `8b      8b,dPPYba,   ,adPPYba,  ,adPPYYba,  
     #8YaaaaY8b     88P'   "Y8  a8P_____88  ""     `Y8  
    #8""""""""8b    88          8PP"""""""  ,adPPPPP88  
   #8'        `8b   88          "8b,   ,aa  88,    ,88  
  #8'          `8b  88           `"Ybbd8"'  `"8bbdP"Y8  
  
  # The Grid Area defines the workable area of the Grid.
  # Dynamic properties do no work here, the Grid Area is like a more specialized, and restrictive Grid Zone  
  
  class GridArea extends Zone
    
    className: () ->
      return 'g_area_' + @cid
    
    itemClassName: () ->
      return 'g_area_' + @cid + '_item'
      
    defaults:
      el            :   null                                                             
      height        :   'auto'  
      width         :   'auto'
      min_height    :   0
      max_height    :   'none' 
      min_width     :   0
      max_width     :   'none'

    
    initialize: () ->
      @attributes = _.defaults @attributes, Zone.prototype.defaults
      super
      

      
      
    ###8888ba,               88           88  
   d8"'    `"8b              ""           88  
  d8'                                     88  
  88             8b,dPPYba,  88   ,adPPYb,88  
  88      88888  88P'   "Y8  88  a8"    `Y88  
  Y8,        88  88          88  8b       88  
   Y8a.    .a88  88          88  "8a,   ,d88  
    `"Y88888P"   88          88   `"8bbdP###

      
  class Grid extends RW.Model
    
    #is_grid: true
    
    bufferSelector: () ->
      return '[data-g-buffer="' + this.id + '"]'
      
    measured: false
    
    defaults:
      el          : null
      id          : null
      dimensions  : null    # @attributes.area.dimensions has models for @dimensions collection
      zoneLayout  : null
      zones       : null    # @attributes.zones has models for @zones collection
      area        :         # @attributes.area is setter for @area model
        top         : 25
        right       : 50
        bottom      : 75
        left        : 100
        min_height  : 250
      
    isAreaReady: false

    initialize: () ->
      _.bindAll this, 'on_window_resize', 'onZoneAdded', 'onZoneRemoved'
      that = this
      
      $(window).bind 'smartresize', () ->
        that.on_window_resize()
            
      el = @get('el')
      $el = $(el)
      @el = el                     
            
      this.bufferEl = $(this.bufferSelector())
      
      this.dimensions = new DimensionCollection()
      
      this.zones = new ZoneCollection()
      this.zones.bind 'add', @onZoneAdded
      this.zones.bind 'remove', @onZoneRemoved
      #this.zones.bind 'refresh', @onZonesRefresh
    
    #/////////////////////////////////////////////////////////// Grid.measure
    measure: () ->            
      $el = $(@el)
      that = this            
      area = @area

      # layout grid area
      area.measure()

      # measure Grid dimensions
      this.dimensions.each (dim) ->
        dim.measure()
      
      # ... hacking with DEFERs! ...
      
      # measure Grid zones
      this.zones.each (zone) ->
        zone.measure()
        # size the zones
        _.defer () -> 
          zone.dynamicallySize()

      # size the area
      _.defer () ->
         area.dynamicallySize()

      # size the grid
      _.defer () ->        
        $el.height($el.childrenHeight() + area.get('bottom')) # .width($(area.el).width() + area.get('left') + area.get('right'))

      @measured = true
    
    #/////////////////////////////////////////////////////////// Grid.setup...
    setup: () ->
      # first setup the area so the dimensions can be measured
      @setupArea()
      # then, setup the dimensions that will use the Grid Area to for its measurements
      @setupDimensions()
      # setup all the statically defined Zones from which dynamic Zones can be generated
      @setupZones()      
      # Dynamically generate Zones
      #@setupZoneGenerators()    
      # place all items into zones
      @populate()
      # now that everything is setup and populated.
      # set valid measurements on the Grid's Area, Dimensions and Zones
      @measure()

    #////////////////////////////////// Grid.setup...
    setupArea: () ->      
      areaSettings = @get 'area'
      # if first time, add the area
      if !@area?
        areaSettings or areaSettings = {}
        areaSettings.grid = this
        area = new GridArea(areaSettings)    
        area.render()     
        $(@el).prepend(area.el)
        @area = area
      # else update the area
      else
        @area.set areaSettings
        
    #////////////////////////////////// Grid.setup...
    # 1. SmartSet @zones collection
    # 2. each zone.setup()
    setupZones: () ->
      that = this
       
      _.attrToSelf_collection_proxy(this, 'zones').smartReset({ add: @addZones })
                        
      # setup all the zones
      for zone in @zones.models
        zone.setup()
      
    populate: () ->            
      for zone in @zones.models
        zone.populate()
        
    refreshZones: (models) ->
      @removeZones this.zones.models
      this.zones.refresh null, {silent:true}
      @addZones( models )      
      
    addZones: (models, addMethod='add') ->
      models = _.flattenModels(models)
      @prepZones(models)
      @zones[addMethod]( models )    
      
    prepZones: (models) ->
      that = this
      if _.isArray models
        _.each models, (model) ->
          that._prepZone(model, models)
      else if _.isObject models
        that._prepZone(models, false)
        
    _prepZone: (model, collection) ->
      model.grid = this              
              
    onZoneAdded: (zone) ->
      parentId = zone.get 'parentId'
      zone.render()
      if !parentId
        parent = $(@area.el)
      else        
        parent = $(this.zones.get(parentId).el)
      $( zone.el ).appendTo( parent )
      
    
    removeZones: (models) ->
      for zone in models
        if zone?.el? then $(zone.el).remove()        
      this.zones.remove models, {silent:true}
      
    
    onZoneRemoved: (zone) ->
    
    # dimensions
    setupDimensions: () ->
      that = this
      # this.attributes.dimensions is a temporary holder of dimensions
      _.attrToSelf_collection_proxy(this, 'dimensions').smartReset({ add: @addDimensions })
    
    refreshDimensions: (models) ->      
      this.dimensions.refresh()
      @addDimensions( models)
      
    addDimensions: (models, addMethod='add') ->
      el = @area.el
      that = this
      # add the el to the dimensions    
      _.each models, (dim) ->
        dim.el = el
        dim.grid = that
      this.dimensions[addMethod]( models )
      # a dash of syntax sugar: make the dimensions available to the G[grid_id][dimension_id]
      _.each models, (dim) ->
        id = dim.id
        #if !that[id]? then that[id] = that.dimensions.get(id).self else ERROR 'G.grid.addDimensions() cant add a dimension with an id of: ' + id
        that[id] = that.dimensions.get(id).self
    
    previewZone:
      id: '_preview'
      width: '100%'
      height: '100%'
    
    guides_shown: false
    guides_el: null
    
    showGuides: () ->
      @guides_shown = true
      @guides_el = $.templates.grid_dimension_preview(this)
      @guides_area_el = $.templates.grid_area_preview()
      @area.place( @guides_area_el)
      @area.place( @guides_el, false )
      
    hideGuides: () ->
      @guides_el.remove()
      @guides_area_el.remove()
      @guides_shown = false
      #_.each this.dimensions.models, (dim) ->
      #  dim_id = dim.id
      #  count = dim.count()
        

  
    on_window_resize: () ->
      # guides
      guides_shown = @guides_shown
      if guides_shown then @hideGuides()
      
      this.measure()
      
      if guides_shown then @showGuides()
    
    template:
      '
        {{>grid_styles}}
        <section class="grid_area">
          {{#fields}}
            {{>fieldPartial}}
          {{/fields}}
        </section>
      '
    grid_styles:
      '
        <style>
          .grid_area {position:absolute; background-color:red;}
        </style>
      '
    fieldPartial:
      '
        <{{tagType}} data-grid-field-name="{{name}}" style="{{css}}">
        </{{tagType}}>
      '
    

  $.tags.add
    name: 'grid_area_preview'
    template:
      '
        <div class="G_area_preview borderBox" style="border:1px solid red; opacity:.3;">
        </div>
      '

  $.tags.add
    name: 'grid_dimension_preview'
    template:
      '
        <div {{isTheTag}} class="G_dimension_preview" style="position:absolute; top:0px; right:0px; bottom:0px; left:0px;">
          {{#dimensions.models}}
            <div style="position:absolute; top:0px; right:0px; bottom:0px; left:0px;">
              {{#previewItems}}              
                {{#is_vertical}}
                  <div style="position:relative; height:{{zoneUnit}}px; width:100%; float:left;">
                    <div class="borderBox" style="border:1px solid red; opacity:.2; left:0px; right:0px; top:{{gutterHalf}}px; bottom:{{gutterHalf}}px; position:absolute;">
                    </div>
                  </div>
                {{/is_vertical}}
                {{#is_horizontal}}
                  <div style="position:relative; width:{{zoneUnit}}px; height:100%; float:left;">
                    <div class="borderBox" style="border:1px solid red; opacity:.2; top:0px; bottom:0px; left:{{gutterHalf}}px; right:{{gutterHalf}}px; position:absolute;">
                    </div>
                  </div>
                {{/is_horizontal}}
              {{/previewItems}}
            </div>
          {{/dimensions.models}}
        </div>
      '
  class GridCollection extends RW.Collection
    model: Grid
    
    initialize: () ->
      that = this
      
      ###
      # API Sugar: allow easy Grid retrieval by G[id] = grid
      this.bind 'add', (grid) ->
        id = grid.id or ERROR '$().G() ...  yo grid needs an id'
        if !_.isString id then ERROR '$().G() ...  yo grids id must be a String'
        if G.id? then ERROR '$().G() ...  grid id is invalid'
        G[id] = grid
      ###
  
  
  G.grids = new GridCollection
  
  G.addGrid = (model) ->
    
    id = model.id or ERROR '$().G() ...  yo grid needs an id'
    if !_.isString id then ERROR '$().G() ...  yo grids id must be a String'
    if G.id? then ERROR '$().G() ...  grid id is invalid'
    G[id] = new Grid( model )
    grid = G[id]
    G.grids.add( grid )
    # have to stagger the setup method so everything is initted
    
    return grid



  
  $.plugins.add
    name: 'G'
    use$prite: false
    zones: null
    setupOnce: false
    grid: null
    api:
      id: null
      layout: 'float'  #  vbox  hbox  masonry  positioned
      children: null
      ###
        [
          {id:'title', width:'100%', height:G.v(10)  }
          {id:'nav',  w:'100%', height:G.v(3)    }
          {id:'content', w:'100%', height:'auto'}
        ]
      ###
    grid: (test = 'fail') ->
      alert test
      BP this
    
    # called on item only the first time...
    setup: (el) ->      
      $el = $(el)      
      
      is_zone = $el['is']('[data-g-zone]')
      
      grid = G.addGrid( _.defaults({el:el}, this.attributes) )
      this.grid  = grid
      
    update: (el) ->
      grid = this.grid

      if this.setupOnce        
        grid.set this.attributes
        
      grid.setup()
      this.setupOnce = true
      #if is_zone
      
      #else


  
  
      
  $.plugins.add
    name: 'childrenHeight'
    returnsValue: true
    get: (el) ->
      $el = $(el)  
      children = $el.children()
      currentHeight = 0
      _.each children, (child) ->
        $child = $(child)
        h = $child.position().top + $child.height()
        if h > currentHeight then currentHeight = h
      return currentHeight
      
  
  $.plugins.add
    name: 'childrenWidth'
    returnsValue: true
    get: (el) ->
      $el = $(el)  
      children = $el.children()
      currentWidth = 0
      _.each children, (child) ->
        $child = $(child)
        w = $child.position().left + $child.width()
        if w > currentWidth then currentWidth = w
      return currentWidth
    

  




  
  
  

  ###

   * JQuery Plugin: "EqualHeights"
   * by:	Scott Jehl, Todd Parker, Maggie Costello Wachs (http://www.filamentgroup.com)
   *
   * Copyright (c) 2008 Filament Group
   * Licensed under GPL (http://www.opensource.org/licenses/gpl-license.php)
   *
   * Description: Compares the heights or widths of the top-level children of a provided element 
   		and sets their min-height to the tallest height (or width to widest width). Sets in em units 
   		by default if pxToEm() method is available.
   * Dependencies: jQuery library, pxToEm method	(article: 
  		http://www.filamentgroup.com/lab/retaining_scalable_interfaces_with_pixel_to_em_conversion/)							  
   * Usage Example: $(element).equalHeights();
    		Optional: to set min-height in px, pass a true argument: $(element).equalHeights(true);
   * Version: 2.0, 08.01.2008


  $.fn.equalHeights = function(px) {
  	$(this).each(function(){
  		var currentTallest = 0;
  		$(this).children().each(function(i){
  			if ($(this).height() > currentTallest) { currentTallest = $(this).height(); }
  		});
  		if (!px || !Number.prototype.pxToEm) currentTallest = currentTallest.pxToEm(); //use ems unless px is specified
  		// for ie6, set height since min-height isn't supported
  		if ($.browser.msie && $.browser.version == 6.0) { $(this).children().css({'height': currentTallest}); }
  		$(this).children().css({'min-height': currentTallest}); 
  	});
  	return this;
  };
  
  
  ###
  


# Global Variables
# ====================
# Global variable scoping, setup for use with either CommonJs or Node.js!
# Coffeescript limits global variable polluting, so if one is needed, just use root.(variabile name)
# - [stackoverflow](http://stackoverflow.com/questions/4214731/coffeescript-global-variables)
root = exports ? this