fannypack 0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.ruby-version +1 -0
  4. data/Gemfile +8 -0
  5. data/Gemfile.lock +127 -0
  6. data/Guardfile +6 -0
  7. data/LICENSE +22 -0
  8. data/Procfile +1 -0
  9. data/README.md +166 -0
  10. data/Rakefile +50 -0
  11. data/config.rb +74 -0
  12. data/fannypack.gemspec +22 -0
  13. data/lib/assets/javascripts/fanny_pack.coffee +21 -0
  14. data/lib/assets/javascripts/fanny_pack/ajax.js.coffee +58 -0
  15. data/lib/assets/javascripts/fanny_pack/env.js.coffee +4 -0
  16. data/lib/assets/javascripts/fanny_pack/models/environment.js.coffee +15 -0
  17. data/lib/assets/javascripts/fanny_pack/namespace.coffee +10 -0
  18. data/lib/assets/javascripts/fanny_pack/routers/base.coffee +82 -0
  19. data/lib/assets/javascripts/fanny_pack/version.js.coffee +2 -0
  20. data/lib/assets/javascripts/fanny_pack/views/app.coffee +19 -0
  21. data/lib/assets/javascripts/fanny_pack/views/base.coffee +90 -0
  22. data/lib/assets/javascripts/fanny_pack/views/list.coffee +29 -0
  23. data/lib/fanny_pack.rb +4 -0
  24. data/lib/fanny_pack/assets.rb +24 -0
  25. data/lib/fanny_pack/version.rb +4 -0
  26. data/source/images/background.png +0 -0
  27. data/source/images/middleman.png +0 -0
  28. data/source/index.html.haml +9 -0
  29. data/source/javascripts/application.js.coffee +2 -0
  30. data/source/javascripts/lib/.gitkeep +0 -0
  31. data/source/javascripts/vendor/.gitkeep +0 -0
  32. data/source/layouts/layout.html.haml +49 -0
  33. data/source/stylesheets/application.css.sass +2 -0
  34. data/source/stylesheets/lib/_fonts.css.sass +0 -0
  35. data/source/stylesheets/lib/_mixins.css.sass +0 -0
  36. data/source/stylesheets/lib/_variables.css.sass +0 -0
  37. data/source/stylesheets/lib/common.css.sass +1 -0
  38. data/source/stylesheets/vendor/all.css +55 -0
  39. data/source/stylesheets/vendor/normalize.css +375 -0
  40. data/spec/javascripts/helpers/spec_helper.coffee +11 -0
  41. data/spec/javascripts/support/jasmine-jquery.js +812 -0
  42. data/spec/javascripts/support/jasmine-sinon.coffee +246 -0
  43. data/spec/javascripts/support/jasmine.yml +10 -0
  44. data/spec/javascripts/support/jasmine_helper.rb +3 -0
  45. data/spec/javascripts/support/sinon-1.7.3.js +4290 -0
  46. data/spec/javascripts/views/base_spec.coffee +130 -0
  47. data/spec/javascripts/views/list_spec.coffee +31 -0
  48. data/vendor/assets/javascripts/backbone-1.1.2.js +1608 -0
  49. data/vendor/assets/javascripts/jquery-1.11.0.js +10337 -0
  50. data/vendor/assets/javascripts/json2.js +487 -0
  51. data/vendor/assets/javascripts/ppx.js +391 -0
  52. data/vendor/assets/javascripts/underscore-1.5.1.js +1246 -0
  53. data/vendor/assets/javascripts/uri.js +1803 -0
  54. data/vendor/assets/javascripts/zepto-detect.js +66 -0
  55. metadata +150 -0
data/fannypack.gemspec ADDED
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/fanny_pack/version', __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "fannypack"
6
+ s.version = FannyPack::VERSION
7
+ s.authors = ["Matt Diebolt", "Steel Fu", "Brad Gessler"]
8
+ s.email = ["matt@polleverywhere.com", "steel@polleverywhere.com", "brad@polleverywhere.com"]
9
+ s.homepage = "http://getfannypack.com"
10
+ s.summary = %q{A simple set of base views to help develop Backbone applications.}
11
+ s.description = %q{A simple set of base views to help develop Backbone applications.}
12
+
13
+ # Manifest
14
+ s.files = `git ls-files`.split("\n")
15
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
17
+ s.require_paths = ["lib"]
18
+
19
+ s.add_development_dependency 'middleman', '~> 3.3'
20
+ s.add_development_dependency 'growl'
21
+ s.add_development_dependency 'rb-fsevent'
22
+ end
@@ -0,0 +1,21 @@
1
+ # Handle lots of commond URL parsing concerns so that we can easily
2
+ # do stuff like URI('google.com/hi').params({search: 'football'})
3
+ #= require uri
4
+
5
+ # IE8 JSON parsing compatability.
6
+ # TODO - Remove when we no longer care about IE8.
7
+ #= require json2
8
+
9
+ # Provides $.os support, which jQuery ditched.
10
+ #= require zepto-detect
11
+
12
+ # Fanny Pack prerequisites
13
+ #= require fanny_pack/namespace
14
+ #= require fanny_pack/version
15
+ #= require fanny_pack/models/environment
16
+ #= require fanny_pack/env
17
+
18
+ # Fanny Pack assets
19
+ #= require_tree ./fanny_pack/routers
20
+ #= require_tree ./fanny_pack/models
21
+ #= require_tree ./fanny_pack/views
@@ -0,0 +1,58 @@
1
+ #= require ppx
2
+
3
+ FannyPack.namespace "FannyPack", (FannyPack) ->
4
+ FannyPack.BasicAuth =
5
+ setCredentials: (username, password) ->
6
+ FannyPack.BasicAuth.username = username
7
+ FannyPack.BasicAuth.password = password
8
+ FannyPack.AJAX_DEFAULTS.headers['Authorization'] = "Basic #{btoa(FannyPack.BasicAuth.username + ':' + FannyPack.BasicAuth.password)}"
9
+
10
+ FannyPack.AJAX_DEFAULTS =
11
+ xhrFields: {}
12
+ headers: {}
13
+ dataType: 'json'
14
+ type: 'GET'
15
+
16
+ # Deep clone the ajax defaults
17
+ ajaxDefaults = ->
18
+ clone = _.clone FannyPack.AJAX_DEFAULTS
19
+ clone.xhrFields = _.clone FannyPack.AJAX_DEFAULTS.xhrFields
20
+ clone.headers = _.clone FannyPack.AJAX_DEFAULTS.headers
21
+ clone
22
+
23
+ # Cross Domain polyfill for IE8
24
+ # ---
25
+ # IMPORTANT: We MUST call toString() on this method or IE will barf on allow
26
+ # other URLS to access the domain. For example, IE would request the URL
27
+ # 'multiple_choice_polls/[object]' without throwing an error message.
28
+
29
+ # PPX Proxy JS will determine whether iframe needs SSL or not
30
+ polyfillCORS = (options, xhr) ->
31
+ # A PPX end-point lives in our rails_app server at the /ppx location.
32
+ path = FannyPack.env.get('api_url').path()
33
+ ppxUrl = FannyPack.env.get('api_url').path("/ppx#{path}")
34
+
35
+ FannyPack.PPXRequest ||= PPX.buildClientConstructor ppxUrl.toString()
36
+
37
+ if (options.crossDomain && !$.support.cors) || options.usePostMessage
38
+ options.xhr = FannyPack.PPXRequest
39
+ options.crossDomain = false
40
+ xhr.isProxiedThroughPostMessage = true
41
+
42
+ $.ajaxPrefilter (options, originalOptions, xhr) ->
43
+ _.defaults options, ajaxDefaults()
44
+
45
+ # Only set this up if we're given a relative path. Paths with domains
46
+ # should just flow through to the next uri mutations.
47
+ url = URI(options.url).absoluteTo(FannyPack.env.get('api_url'))
48
+ url.setQuery 'cache_buster', (new Date).getTime().toString() if $.os?.android
49
+ options.url = url.toString()
50
+
51
+ # Check if this is a cross domain request.
52
+ # CORS PPX polyfill uses this to determine if PPX should be used.
53
+ options.crossDomain = !url.absoluteTo('/').equals(URI(window.location.href).absoluteTo('/'))
54
+
55
+ # We need this for CORS + Cookies to work when using jQuery
56
+ options.xhrFields.withCredentials ?= true
57
+
58
+ polyfillCORS(options, xhr)
@@ -0,0 +1,4 @@
1
+ # Setup an environment class for all Poll Everywhere applications to access and store global state.
2
+
3
+ FannyPack.namespace "FannyPack", (FannyPack) ->
4
+ FannyPack.env = new FannyPack.Model.Environment
@@ -0,0 +1,15 @@
1
+ FannyPack.namespace "FannyPack.Model", (Model) ->
2
+ class Model.Environment extends Backbone.Model
3
+ currentHost = window?.location?.host
4
+ protocol = window?.location?.protocol
5
+ defaults:
6
+ api_url: "//#{currentHost}"
7
+ ssl: protocol is 'https:'
8
+
9
+ get: (key) ->
10
+ switch
11
+ # When a key ends with _url, wrap it in an URI.js object so we can mutate it.
12
+ when key.match(/_url$/)
13
+ URI(super(key))
14
+ else
15
+ super(key)
@@ -0,0 +1,10 @@
1
+ if module?.exports?
2
+ global.FannyPack ||= {}
3
+ else
4
+ window.FannyPack ||= {}
5
+
6
+ FannyPack.namespace = (target, name, block) ->
7
+ [target, name, block] = [(if global? then global else window), arguments...]
8
+ top = target
9
+ target = target[item] or= {} for item in name.split '.'
10
+ block target, name
@@ -0,0 +1,82 @@
1
+ # The FannyPack.Router.Base is considered our most global application
2
+ # state for applications. Its assumed that only one router should
3
+ # be running at a given time within the context of a single runtime.
4
+ #
5
+ # Views and Templates all have access to the router via the `@app`
6
+ # method.
7
+ FannyPack.namespace "FannyPack.Router", (Router) ->
8
+ {Model} = FannyPack
9
+
10
+ class Router.Base extends Backbone.Router
11
+ # Default app name. Sets the default <title> on the page and
12
+ # is used in error messaging.
13
+ appName: "FannyPack App"
14
+
15
+ constructor: ->
16
+ # Lets prevent confusion for the clowns out there who try
17
+ # initializing 2 routers in the same runtime.
18
+ throw Error "FannyPack.app already initialized with '#{@appName}'" if FannyPack.app
19
+ FannyPack.app = @
20
+
21
+ # Convient access to the FannyPack.env from
22
+ # applications
23
+ @env = FannyPack.env
24
+
25
+ # What's that? You turned logging on? SWEET! You
26
+ # get to hear about everything that's happening in the
27
+ # event bus.
28
+ if @env.get('logging_enabled')
29
+ @on 'all', =>
30
+ @log 'App Event', arguments
31
+
32
+ # View manager responsible for de-activating old views and activating new ones
33
+ @view = new View.App
34
+
35
+ # We call super last since this calls the `initialize` method,
36
+ # which assumes everything above is all set and ready to go.
37
+ super
38
+
39
+ # Take care of all the messy bootstrapping of browser globals and singletons to
40
+ # the router, render a view, bootstrap user data from the server, and start
41
+ # Backbone.history routing. Basically all of the gross browser bootstrapping should
42
+ # go here.
43
+ #
44
+ # If you're trying to setup the router for testing, you should either decompose this
45
+ # method or manually start the browser's history.
46
+ start: (opts={pushState: true}) =>
47
+ throw Error "Specify a view property in the initialize method of the router" unless @view
48
+
49
+ {pushState, el} = opts
50
+
51
+ # Render the view first on the element since the login state could
52
+ # change what's rendered.
53
+ el.append @view.render().$el
54
+
55
+ # Start the router
56
+ Backbone.history.start(pushState: pushState)
57
+
58
+ @
59
+
60
+ # Returns the current location of the application.
61
+ location: ->
62
+ # This assumes that we're in a web browser context.
63
+ window.location.pathname.substring(1)
64
+
65
+ # Go "back" through the browsers history.
66
+ navigateBack: ->
67
+ Backbone.history.history.back()
68
+
69
+ # Default in backbone is to *not* trigger to execute the route handler,
70
+ # which is undesirable and feels like unexpected behavior for our devs.
71
+ navigate: (fragment, options = {trigger: true}) ->
72
+ super fragment, options
73
+
74
+ # Log tracing messages to a logging device if enabled.
75
+ log: =>
76
+ console.log(arguments...) if @env.get('logging_enabled')
77
+
78
+ # Change the title of the page and notify views/models that
79
+ # care about a title change what's up.
80
+ title: (title = @appName) =>
81
+ document?.title = title
82
+ @trigger "change:title", title
@@ -0,0 +1,2 @@
1
+ FannyPack.namespace "FannyPack", (Package) ->
2
+ Package.version = '0.2' # This updates the fanny_pack.gemspec version
@@ -0,0 +1,19 @@
1
+ #= require_tree ../views
2
+
3
+ FannyPack.namespace "FannyPack.View", (View) ->
4
+ class View.App extends View.Base
5
+ className: 'fannypack'
6
+
7
+ initialize: (opts = {}) ->
8
+ @currentView = null
9
+
10
+ activate: (view) =>
11
+ @currentView?.remove()
12
+ @currentView = view
13
+ @$el.append view.render().$el
14
+
15
+ currentPage: =>
16
+ @currentView
17
+
18
+ render: =>
19
+ @
@@ -0,0 +1,90 @@
1
+ FannyPack.namespace 'FannyPack.View', (View) ->
2
+ class View.Base extends Backbone.View
3
+ include: (modules...) ->
4
+ for module in modules
5
+ _.extend this, module(this)
6
+
7
+ constructor: (options = {}) ->
8
+ # Set the app for this view to the base application
9
+ # that FannyPack.Router.Base sets up in its initialize method.
10
+ @app ||= FannyPack.app
11
+
12
+ {title, template} = options
13
+
14
+ # Set the page title if we're given one.
15
+ @title = title if title?
16
+
17
+ # Override the template if specified
18
+ @template = template if template?
19
+
20
+ # TODO - Figure out how to show the full namespace
21
+ # of the view that's being rendered (and not just the prototype name)
22
+ # Make it easier for non-lib devs to find views.
23
+ @app.log "Constructing View", @
24
+
25
+ # Tell the router what the title of our window should be.
26
+ @app.title @title if @title
27
+
28
+ # make sure we have an external events hash
29
+ # if the child class hasn't defined one
30
+ @appEvents ?= {}
31
+
32
+ # Setup Backbone.View last since it fires the
33
+ # subclasses' `initialize` method, which assumes
34
+ # everything above is setup.
35
+ super
36
+
37
+ # Listen to a list of events and fire callback only once
38
+ # e.g. @listenToChanges @poll, "max_votes web_enabled sms_enabled", @refreshPage
39
+ # The above will call @refreshPage only once instead of 3 times for each attribute
40
+ # change event
41
+ listenToChanges: (model, attributes, callback) =>
42
+ attributes = attributes.split /\s+/
43
+ @listenTo model, 'change', (model) ->
44
+ changedAttributes = _.keys model.changed
45
+
46
+ if _.intersection(changedAttributes, attributes).length
47
+ callback.apply(@, model)
48
+
49
+ pageTitle: =>
50
+ if _.isFunction(@title)
51
+ @title.apply(@)
52
+ else
53
+ @title
54
+
55
+ _configureAppEvents: =>
56
+ for action, handler of @appEvents
57
+ try
58
+ method = @[handler]
59
+ method = handler if _.isFunction(handler)
60
+ boundMethod = _.bind(method, @)
61
+
62
+ @app.on action, boundMethod
63
+ catch
64
+ name = "'#{@constructor?.name || ''}'"
65
+ message = "View #{name} has no method named '#{handler}'. It cannot be registered as a handler for application events."
66
+ throw new Error(message)
67
+
68
+ @
69
+
70
+ delegateEvents: (events) =>
71
+ super
72
+ @_configureAppEvents()
73
+ @
74
+
75
+ undelegateEvents: =>
76
+ super
77
+ for action, handler of @appEvents
78
+ @app.off action, _.bind @[handler], @
79
+ @
80
+
81
+ renderTemplate: (template = @template) =>
82
+ @app.log "Rendering Template", template
83
+ @$el.html JST[template]()
84
+
85
+ renderTitle: =>
86
+ @$(".title").text @pageTitle()
87
+ @app.title @pageTitle()
88
+
89
+ pageClass: =>
90
+ ""
@@ -0,0 +1,29 @@
1
+ #= require ../namespace
2
+
3
+ FannyPack.namespace 'FannyPack.View', (View) ->
4
+ class View.List extends View.Base
5
+ initialize: (options={}) ->
6
+ @collection = options.collection
7
+ @subView = options.subView
8
+
9
+ @subViews = []
10
+
11
+ # always blow away existing subViews.
12
+ # call this only if you want a full re-render
13
+ render: =>
14
+ @remove()
15
+
16
+ @collection.each (item) =>
17
+ v = new @subView
18
+ model: item
19
+
20
+ @subViews.push v
21
+
22
+ @$el.append v.render().$el
23
+
24
+ @
25
+
26
+ remove: =>
27
+ _.invoke @subViews, 'remove'
28
+
29
+ super
data/lib/fanny_pack.rb ADDED
@@ -0,0 +1,4 @@
1
+ require "fanny_pack/version"
2
+ require "fanny_pack/assets"
3
+
4
+ FannyPack::Assets::Sprockets.auto_detect
@@ -0,0 +1,24 @@
1
+ module FannyPack
2
+ # Deal with bundling Sprocket assets into environments (like Rails or Sprockets)
3
+ module Assets
4
+ def self.path(*segs)
5
+ File.join File.expand_path('../../assets', __FILE__), segs
6
+ end
7
+
8
+ # Integrate FannyPack ./lib/assets files into a sprocket-enabled environment.
9
+ module Sprockets
10
+ # Drop flash and javascript paths to FannyPack assets into a sprockets environment.
11
+ def self.configure(env)
12
+ env.append_path FannyPack::Assets.path('javascripts')
13
+ env
14
+ end
15
+
16
+ # Try to automatically configure Sprockets if its detected in the project.
17
+ def self.auto_detect
18
+ if defined? ::Sprockets and ::Sprockets.respond_to? :append_path
19
+ FannyPack::Assets::Sprockets.configure ::Sprockets
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,4 @@
1
+ module FannyPack
2
+ # Read the FannyPack.version attribute from lib/assets/javascripts/fanny_pack/version.js.coffee
3
+ VERSION = File.read(File.expand_path('../../assets/javascripts/fanny_pack/version.js.coffee', __FILE__)).match(/Package.version \= '(.+)'/m).captures.first
4
+ end
Binary file
Binary file
@@ -0,0 +1,9 @@
1
+ ---
2
+ title: Middleman is Watching
3
+ body_class: index
4
+ ---
5
+
6
+ .welcome
7
+ %h1 Middleman is Watching
8
+ %p.doc
9
+ = link_to 'Read Online Documentation', 'http://middlemanapp.com/', title: 'Read Online Documentation', target: '_blank'
@@ -0,0 +1,2 @@
1
+ #= require_tree ./vendor
2
+ #= require_tree ./lib
File without changes
File without changes
@@ -0,0 +1,49 @@
1
+ !!! 5
2
+ /[if lt IE 7] <html class="no-js lt-ie10 lt-ie9 lt-ie8 lt-ie7" lang="en-us">
3
+ /[if IE 7] <html class="no-js lt-ie10 lt-ie9 lt-ie8" lang="en-us">
4
+ /[if IE 8] <html class="no-js lt-ie10 lt-ie9" lang="en-us">
5
+ /[if IE 9] <html class="no-js lt-ie10 lt-ie9" lang="en-us">
6
+ /[if lt IE 10] <html class="no-js lt-ie10" lang="en-us">
7
+ /[if !IE]>
8
+ %html{lang: 'en', class: 'no-js'}
9
+ /<![endif]
10
+ %head
11
+ %title #{data.page.title || "The Middleman"}
12
+ %meta(http-equiv="content-type" content="text/html" charset="utf-8")
13
+ %meta(http-equiv="x-ua-compatible" content="ie=edge,chrome=1")
14
+ %meta(name="description" content="")
15
+ %meta(name="author" content="")
16
+
17
+ -# Standard viewport tag to set the viewport to the device's width,
18
+ -# Android 2.3 devices need this so 100% width works properly and
19
+ -# doesn't allow children to blow up the viewport width
20
+ %meta{content: "initial-scale=1.0,user-scalable=no,maximum-scale=1,width=device-width", name: "viewport"}
21
+
22
+ -# Fix for iPhone 5 fullscreen web apps
23
+ %meta(name="viewport" content="initial-scale=1.0,user-scalable=no,maximum-scale=1" media="(device-height: 568px)")
24
+ %meta(name='apple-mobile-web-app-capable' content='yes')
25
+ %meta(name='apple-mobile-web-app-status-bar-style' content='translucent-black')
26
+
27
+ -# App Icons
28
+ %link(rel="shortcut icon" href="/images/favicon.ico")
29
+ %link(rel="apple-touch-icon-precomposed" href="/images/apple-touch-icon-precomposed.png")
30
+ %link(rel="apple-touch-icon-precomposed" sizes="57x57" href="/images/apple-touch-icon-57x57-precomposed.png")
31
+ %link(rel="apple-touch-icon-precomposed" sizes="72x72" href="/images/apple-touch-icon-72x72-precomposed.png")
32
+ %link(rel="apple-touch-icon-precomposed" sizes="114x114" href="/images/apple-touch-icon-114x114-precomposed.png")
33
+
34
+ -# Stylesheets
35
+ = stylesheet_link_tag 'application'
36
+ %body{class: current_page.data.body_class || 'page'}
37
+ = yield
38
+ -# Javascripts
39
+ = javascript_include_tag '//cdnjs.cloudflare.com/ajax/libs/modernizr/2.6.2/modernizr.min.js'
40
+ = javascript_include_tag '//cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js'
41
+ = javascript_include_tag 'application'
42
+
43
+ - if build?
44
+ -# Google Analytics
45
+ :javascript
46
+ var _gaq=[['_setAccount','UA-XXXXX-X'],['_trackPageview']];
47
+ (function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];
48
+ g.src=('https:'==location.protocol?'//ssl':'//www')+'.google-analytics.com/ga.js';
49
+ s.parentNode.insertBefore(g,s)}(document,'script'));