fannypack 0.2

Sign up to get free protection for your applications and to get access to all the features.
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'));