├── img ├── oops.jpg ├── sociogram.jpg ├── sociogram_100x108.png ├── sociogram_128x128.png ├── sociogram_136x88.png ├── sociogram_155x100.png ├── sociogram_16x16.png ├── sociogram_180x115.png ├── sociogram_204x132.png ├── sociogram_272x176.png ├── sociogram_394x150.png ├── sociogram_60x65.png ├── sociogram_64x64.png ├── sociogram_75x75.png ├── sociogram_800x150.png ├── sociogram_80x86.png └── sociogram_96x96.png ├── tpl ├── login.html ├── postui.html ├── welcome.html ├── feed.html ├── friends.html ├── person.html ├── post.html ├── revoke.html ├── error.html └── menu.html ├── js ├── facebook.js ├── utils.js ├── views.js └── app.js ├── css ├── styles.css ├── pageslider.css └── ratchet.css ├── index.html └── lib ├── cdv-plugin-fb-connect.js ├── pageslider.js ├── underscore-min.js ├── backbone-min.js ├── fastclick.js └── ratchet.js /img/oops.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/sociogram-mobile/HEAD/img/oops.jpg -------------------------------------------------------------------------------- /img/sociogram.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/sociogram-mobile/HEAD/img/sociogram.jpg -------------------------------------------------------------------------------- /img/sociogram_100x108.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/sociogram-mobile/HEAD/img/sociogram_100x108.png -------------------------------------------------------------------------------- /img/sociogram_128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/sociogram-mobile/HEAD/img/sociogram_128x128.png -------------------------------------------------------------------------------- /img/sociogram_136x88.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/sociogram-mobile/HEAD/img/sociogram_136x88.png -------------------------------------------------------------------------------- /img/sociogram_155x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/sociogram-mobile/HEAD/img/sociogram_155x100.png -------------------------------------------------------------------------------- /img/sociogram_16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/sociogram-mobile/HEAD/img/sociogram_16x16.png -------------------------------------------------------------------------------- /img/sociogram_180x115.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/sociogram-mobile/HEAD/img/sociogram_180x115.png -------------------------------------------------------------------------------- /img/sociogram_204x132.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/sociogram-mobile/HEAD/img/sociogram_204x132.png -------------------------------------------------------------------------------- /img/sociogram_272x176.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/sociogram-mobile/HEAD/img/sociogram_272x176.png -------------------------------------------------------------------------------- /img/sociogram_394x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/sociogram-mobile/HEAD/img/sociogram_394x150.png -------------------------------------------------------------------------------- /img/sociogram_60x65.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/sociogram-mobile/HEAD/img/sociogram_60x65.png -------------------------------------------------------------------------------- /img/sociogram_64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/sociogram-mobile/HEAD/img/sociogram_64x64.png -------------------------------------------------------------------------------- /img/sociogram_75x75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/sociogram-mobile/HEAD/img/sociogram_75x75.png -------------------------------------------------------------------------------- /img/sociogram_800x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/sociogram-mobile/HEAD/img/sociogram_800x150.png -------------------------------------------------------------------------------- /img/sociogram_80x86.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/sociogram-mobile/HEAD/img/sociogram_80x86.png -------------------------------------------------------------------------------- /img/sociogram_96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccoenraets/sociogram-mobile/HEAD/img/sociogram_96x96.png -------------------------------------------------------------------------------- /tpl/login.html: -------------------------------------------------------------------------------- 1 | <% if (id) { %> 2 |
  • 3 | 4 | <%= name %> 5 |
  • 6 |
  • Logout
  • 7 | <% } else { %> 8 |
  • Login
  • 9 | <% } %> 10 | -------------------------------------------------------------------------------- /tpl/postui.html: -------------------------------------------------------------------------------- 1 |
    2 | Back 3 |

    Sociogram

    4 |
    5 | 6 |
    7 |

    FB.ui is a generic helper method for triggering Dialogs which allow the user to take some action.

    8 |
    9 | Invoke the Post Dialog 10 |
    11 | 12 | -------------------------------------------------------------------------------- /js/facebook.js: -------------------------------------------------------------------------------- 1 | // Wrap FB.api with a Deferred 2 | fbWrapper = { 3 | 4 | api: function(url) { 5 | var deferred = $.Deferred(); 6 | try { 7 | console.log('calling fb api'); 8 | FB.api(url, function (response) { 9 | deferred.resolve(response); 10 | }); 11 | } catch (e) { 12 | deferred.fail(); 13 | } 14 | return deferred; 15 | } 16 | 17 | } -------------------------------------------------------------------------------- /tpl/welcome.html: -------------------------------------------------------------------------------- 1 |
    2 |

    Sociogram

    3 |
    4 | 5 |
    6 | 7 | 8 | 9 |
    10 |

    Sociogram Mobile is a sample application that demonstrates how to use the Facebook JavaScript SDK and the Graph API in a PhoneGap application.

    11 |
    12 | 13 |
    14 | 15 |
    -------------------------------------------------------------------------------- /tpl/feed.html: -------------------------------------------------------------------------------- 1 |
    2 | Back 3 |

    Feed

    4 | Menu 5 |
    6 |
    7 | {{#if .}} 8 | 18 | {{/if}} 19 |
    -------------------------------------------------------------------------------- /tpl/friends.html: -------------------------------------------------------------------------------- 1 |
    2 | Back 3 |

    Friends

    4 | Menu 5 |
    6 | 7 |
    8 | 9 | {{#if .}} 10 | 21 | {{/if}} 22 | 23 |
    -------------------------------------------------------------------------------- /tpl/person.html: -------------------------------------------------------------------------------- 1 |
    2 | Back 3 |

    Profile

    4 | Menu 5 |
    6 |
    7 | {{#if .}} 8 | 9 |

    {{ name }}

    10 |

    {{gender}}, {{locale}}, {{timezone}}

    11 |
    12 | Friends 13 | Mutual Friends 14 | Feed 15 | {{/if}} 16 |
    17 | -------------------------------------------------------------------------------- /tpl/post.html: -------------------------------------------------------------------------------- 1 |
    2 | Back 3 |

    Post

    4 |
    5 | 6 |
    7 |
    8 | 9 |
    10 |

    Check Out Sociogram Mobile

    11 |

    A PhoneGap/Facebook starter app

    12 |

    Sociogram is a sample application that demonstrates how to use the Facebook JavaScript SDK and the Graph API.

    13 |
    14 |
    15 | 16 | 17 | 18 | Post 19 | 20 |
    -------------------------------------------------------------------------------- /tpl/revoke.html: -------------------------------------------------------------------------------- 1 |
    2 | Back 3 |

    Sociogram

    4 |
    5 | 6 |
    7 |

    Click the button below to revoke the permissions you granted to this application. This means that you are "deauthorizing" the application to use your Facebook data. 8 | You will have to login and authorize the application again. This is not something you would typically expose in a real-life application, but it is helpful during development in order 9 | for you to recreate and fine tune the login and authorization experience a new user will be exposed to.

    10 |
    11 | 12 | Revoke Permissions 13 | 14 | 15 |
    -------------------------------------------------------------------------------- /tpl/error.html: -------------------------------------------------------------------------------- 1 |
    2 | Menu 3 |

    Sociogram

    4 |
    5 | 6 |
    7 | 8 |

    An error has occurred.

    9 |

    The problem may have been caused by one of the following reasons:

    10 | 15 |
    16 | 17 |
    -------------------------------------------------------------------------------- /css/styles.css: -------------------------------------------------------------------------------- 1 | * { 2 | -webkit-user-select: none; 3 | } 4 | 5 | input, textarea { 6 | -webkit-user-select: auto !important; 7 | } 8 | 9 | a { 10 | -webkit-touch-callout: none; 11 | } 12 | 13 | .list li > a:active { 14 | background-color: #D7D7D7; 15 | } 16 | 17 | .content { 18 | background-color: #EAEAEA; 19 | } 20 | 21 | img.avatar { 22 | width: 150px; 23 | height: 150px; 24 | } 25 | 26 | .fb { 27 | /*font-size: 10px;*/ 28 | color: #727272; 29 | margin-bottom: 20px; 30 | } 31 | 32 | .fb > div { 33 | margin-left: 70px; 34 | } 35 | 36 | .fb img { 37 | width: 60px; 38 | height: 60px; 39 | float: left; 40 | display: inline-block; 41 | } 42 | 43 | .fb .name { 44 | font-weight: bold; 45 | color: #2f3677; 46 | } 47 | 48 | .fb .caption { 49 | margin-bottom: 4px; 50 | } 51 | -------------------------------------------------------------------------------- /tpl/menu.html: -------------------------------------------------------------------------------- 1 |
    2 |

    Sociogram

    3 | Logout 4 |
    5 | 6 |
    7 | 46 |
    47 | -------------------------------------------------------------------------------- /js/utils.js: -------------------------------------------------------------------------------- 1 | var MobileApp = function() { 2 | 3 | this.initialize = function() { 4 | this.models = {}; 5 | this.views = {}; 6 | this.templateLoader = new this.TemplateLoader(); 7 | }; 8 | 9 | this.TemplateLoader = function () { 10 | 11 | this.templates = {}; 12 | 13 | this.load = function (names, callback) { 14 | 15 | var deferreds = [], 16 | self = this; 17 | 18 | $.each(names, function (index, name) { 19 | deferreds.push($.get('tpl/' + name + '.html', function (data) { 20 | self.templates[name] = Handlebars.compile(data); 21 | })); 22 | }); 23 | 24 | $.when.apply(null, deferreds).done(callback); 25 | }; 26 | 27 | // Get template by name from hash of preloaded templates 28 | this.get = function (name) { 29 | return this.templates[name]; 30 | }; 31 | 32 | }; 33 | 34 | this.alert = function(message, title) { 35 | if (typeof(title)==='undefined') title = "Sociogram"; 36 | if (navigator.notification) { 37 | navigator.notification.alert( 38 | message, 39 | null, // callback 40 | title, 41 | 'OK' // Button label 42 | ); 43 | } else { 44 | alert(title + ": " + message); 45 | } 46 | }; 47 | 48 | this.initialize(); 49 | 50 | } -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Sociogram 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
    15 |
    16 | 17 |
    18 | 19 |
    20 |
    21 |
    22 |
    23 |
    24 |
    25 |
    26 |
    27 |
    28 |
    29 |
    30 |
    31 |
    32 |
    33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /lib/cdv-plugin-fb-connect.js: -------------------------------------------------------------------------------- 1 | CDV = ( typeof CDV == 'undefined' ? {} : CDV ); 2 | var cordova = window.cordova || window.Cordova; 3 | CDV.FB = { 4 | init: function(apiKey, fail) { 5 | // create the fb-root element if it doesn't exist 6 | if (!document.getElementById('fb-root')) { 7 | var elem = document.createElement('div'); 8 | elem.id = 'fb-root'; 9 | document.body.appendChild(elem); 10 | } 11 | cordova.exec(function() { 12 | var authResponse = JSON.parse(localStorage.getItem('cdv_fb_session') || '{"expiresIn":0}'); 13 | if (authResponse && authResponse.expirationTime) { 14 | var nowTime = (new Date()).getTime(); 15 | if (authResponse.expirationTime > nowTime) { 16 | // Update expires in information 17 | updatedExpiresIn = Math.floor((authResponse.expirationTime - nowTime) / 1000); 18 | authResponse.expiresIn = updatedExpiresIn; 19 | 20 | localStorage.setItem('cdv_fb_session', JSON.stringify(authResponse)); 21 | FB.Auth.setAuthResponse(authResponse, 'connected'); 22 | } 23 | } 24 | console.log('Cordova Facebook Connect plugin initialized successfully.'); 25 | }, (fail?fail:null), 'org.apache.cordova.facebook.Connect', 'init', [apiKey]); 26 | }, 27 | login: function(params, cb, fail) { 28 | params = params || { scope: '' }; 29 | cordova.exec(function(e) { // login 30 | if (e.authResponse && e.authResponse.expiresIn) { 31 | var expirationTime = e.authResponse.expiresIn === 0 32 | ? 0 33 | : (new Date()).getTime() + e.authResponse.expiresIn * 1000; 34 | e.authResponse.expirationTime = expirationTime; 35 | } 36 | localStorage.setItem('cdv_fb_session', JSON.stringify(e.authResponse)); 37 | FB.Auth.setAuthResponse(e.authResponse, 'connected'); 38 | if (cb) cb(e); 39 | }, (fail?fail:null), 'org.apache.cordova.facebook.Connect', 'login', params.scope.split(',') ); 40 | }, 41 | logout: function(cb, fail) { 42 | cordova.exec(function(e) { 43 | localStorage.removeItem('cdv_fb_session'); 44 | FB.Auth.setAuthResponse(null, 'notConnected'); 45 | if (cb) cb(e); 46 | }, (fail?fail:null), 'org.apache.cordova.facebook.Connect', 'logout', []); 47 | }, 48 | getLoginStatus: function(cb, fail) { 49 | cordova.exec(function(e) { 50 | if (cb) cb(e); 51 | }, (fail?fail:null), 'org.apache.cordova.facebook.Connect', 'getLoginStatus', []); 52 | }, 53 | dialog: function(params, cb, fail) { 54 | cordova.exec(function(e) { // login 55 | if (cb) cb(e); 56 | }, (fail?fail:null), 'org.apache.cordova.facebook.Connect', 'showDialog', [params] ); 57 | } 58 | }; 59 | -------------------------------------------------------------------------------- /lib/pageslider.js: -------------------------------------------------------------------------------- 1 | /* Notes: 2 | * - History management is currently done using window.location.hash. This could easily be changed to use Push State instead. 3 | * - jQuery dependency for now. This could also be easily removed. 4 | */ 5 | 6 | function PageSlider(container) { 7 | 8 | var container = container, 9 | currentPage = null, 10 | stateHistory = []; 11 | 12 | // Use this function if you want PageSlider to automatically determine the sliding direction based on the state history 13 | this.slidePage = function(page) { 14 | 15 | var l = stateHistory.length, 16 | state = window.location.hash; 17 | 18 | if (page === currentPage) { 19 | return; 20 | } 21 | 22 | if (l === 0) { 23 | stateHistory.push(state); 24 | return this.slidePageFrom(page); 25 | } 26 | if (state === stateHistory[l-2]) { 27 | stateHistory.pop(); 28 | return this.slidePageFrom(page, 'left'); 29 | } else { 30 | stateHistory.push(state); 31 | return this.slidePageFrom(page, 'right'); 32 | } 33 | 34 | } 35 | 36 | // Use this function directly if you want to control the sliding direction outside PageSlider 37 | this.slidePageFrom = function(page, from) { 38 | 39 | console.log("slide " + from); 40 | 41 | var deferred = $.Deferred(); 42 | 43 | container.append(page); 44 | 45 | if (!currentPage || !from) { 46 | console.log('no current page'); 47 | currentPage = page; 48 | page.attr("class", "page center"); 49 | console.log('after attr'); 50 | deferred.resolve(); 51 | return deferred; 52 | } 53 | 54 | // Position the page at the starting position of the animation 55 | try { 56 | page.attr("class", "page " + from); 57 | } catch(e) { 58 | console.log("error"); 59 | console.log(e); 60 | } 61 | 62 | currentPage.one('webkitTransitionEnd', function(e) { 63 | $(e.target).remove(); 64 | console.log("page removed"); 65 | deferred.resolve(); 66 | }); 67 | 68 | // Force reflow. More information here: http://www.phpied.com/rendering-repaint-reflowrelayout-restyle/ 69 | container[0].offsetWidth; 70 | 71 | // Position the new page and the current page at the ending position of their animation with a transition class indicating the duration of the animation 72 | page.attr("class", "page transition center"); 73 | currentPage.attr("class", "page transition " + (from === "left" ? "right" : "left")); 74 | currentPage = page; 75 | 76 | return deferred; 77 | }, 78 | 79 | this.resetHistory = function() { 80 | stateHistory = [window.location.hash]; 81 | } 82 | 83 | this.removeCurrentPage = function() { 84 | currentPage.remove(); 85 | currentPage = null; 86 | } 87 | 88 | } -------------------------------------------------------------------------------- /css/pageslider.css: -------------------------------------------------------------------------------- 1 | #container { 2 | position: absolute; 3 | width: 100%; 4 | height: 100%; 5 | overflow: hidden; 6 | } 7 | 8 | .page { 9 | position: absolute; 10 | top: 0; 11 | left: 0; 12 | width: 100%; 13 | height: 100%; 14 | -webkit-transform: translate3d(0, 0, 0); 15 | transform: translate3d(0, 0, 0); 16 | } 17 | 18 | .page.left { 19 | -webkit-transform: translate3d(-100%, 0, 0); 20 | transform: translate3d(-100%, 0, 0); 21 | } 22 | 23 | .page.center { 24 | -webkit-transform: translate3d(0, 0, 0); 25 | transform: translate3d(0, 0, 0); 26 | } 27 | 28 | .page.right { 29 | -webkit-transform: translate3d(100%, 0, 0); 30 | transform: translate3d(100%, 0, 0); 31 | } 32 | 33 | .page.transition { 34 | -webkit-transition-duration: .25s; 35 | transition-duration: .25s; 36 | } 37 | 38 | /* Spinner: http://37signals.com/svn/posts/2577-loading-spinner-animation-using-css-and-webkit */ 39 | div.spinner { 40 | position: absolute; 41 | width: 54px; 42 | height: 54px; 43 | display: inline-block; 44 | 45 | left: 50%; 46 | top: 50%; 47 | margin: -54px 0 0 -27px; 48 | 49 | } 50 | 51 | div.spinner div { 52 | width: 12%; 53 | height: 26%; 54 | background: #666; 55 | position: absolute; 56 | left: 44.5%; 57 | top: 37%; 58 | opacity: 0; 59 | -webkit-animation: fade 1s linear infinite; 60 | -webkit-border-radius: 50px; 61 | -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.2); 62 | } 63 | 64 | div.spinner div.bar1 { 65 | -webkit-transform: rotate(0deg) translate(0, -142%); 66 | -webkit-animation-delay: 0s; 67 | } 68 | 69 | div.spinner div.bar2 { 70 | -webkit-transform: rotate(30deg) translate(0, -142%); 71 | -webkit-animation-delay: -0.9167s; 72 | } 73 | 74 | div.spinner div.bar3 { 75 | -webkit-transform: rotate(60deg) translate(0, -142%); 76 | -webkit-animation-delay: -0.833s; 77 | } 78 | 79 | div.spinner div.bar4 { 80 | -webkit-transform: rotate(90deg) translate(0, -142%); 81 | -webkit-animation-delay: -0.75s; 82 | } 83 | 84 | div.spinner div.bar5 { 85 | -webkit-transform: rotate(120deg) translate(0, -142%); 86 | -webkit-animation-delay: -0.667s; 87 | } 88 | 89 | div.spinner div.bar6 { 90 | -webkit-transform: rotate(150deg) translate(0, -142%); 91 | -webkit-animation-delay: -0.5833s; 92 | } 93 | 94 | div.spinner div.bar7 { 95 | -webkit-transform: rotate(180deg) translate(0, -142%); 96 | -webkit-animation-delay: -0.5s; 97 | } 98 | 99 | div.spinner div.bar8 { 100 | -webkit-transform: rotate(210deg) translate(0, -142%); 101 | -webkit-animation-delay: -0.41667s; 102 | } 103 | 104 | div.spinner div.bar9 { 105 | -webkit-transform: rotate(240deg) translate(0, -142%); 106 | -webkit-animation-delay: -0.333s; 107 | } 108 | 109 | div.spinner div.bar10 { 110 | -webkit-transform: rotate(270deg) translate(0, -142%); 111 | -webkit-animation-delay: -0.25s; 112 | } 113 | 114 | div.spinner div.bar11 { 115 | -webkit-transform: rotate(300deg) translate(0, -142%); 116 | -webkit-animation-delay: -0.1667s; 117 | } 118 | 119 | div.spinner div.bar12 { 120 | -webkit-transform: rotate(330deg) translate(0, -142%); 121 | -webkit-animation-delay: -0.0833s; 122 | } 123 | 124 | @-webkit-keyframes fade { 125 | from { 126 | opacity: 1; 127 | } 128 | to { 129 | opacity: 0.25; 130 | } 131 | } -------------------------------------------------------------------------------- /js/views.js: -------------------------------------------------------------------------------- 1 | fb.views.Menu = Backbone.View.extend({ 2 | 3 | initialize: function () { 4 | this.template = fb.templateLoader.get('menu'); 5 | this.render(); 6 | }, 7 | 8 | render: function () { 9 | this.$el.html(this.template()); 10 | return this; 11 | } 12 | 13 | }); 14 | 15 | fb.views.Welcome = Backbone.View.extend({ 16 | 17 | initialize: function () { 18 | var self = this; 19 | this.template = fb.templateLoader.get('welcome'); 20 | this.render(); 21 | }, 22 | 23 | render: function () { 24 | this.$el.html(this.template()); 25 | return this; 26 | }, 27 | 28 | events: { 29 | 'click .login': 'login' 30 | }, 31 | 32 | login: function () { 33 | $(document).trigger('login'); 34 | return false; 35 | } 36 | 37 | }); 38 | 39 | fb.views.Person = Backbone.View.extend({ 40 | 41 | initialize: function () { 42 | this.render(); 43 | }, 44 | 45 | render: function () { 46 | this.$el.html(this.options.template(this.model)); 47 | return this; 48 | } 49 | 50 | }); 51 | 52 | fb.views.Friends = Backbone.View.extend({ 53 | 54 | initialize: function () { 55 | this.render(); 56 | }, 57 | 58 | render: function () { 59 | this.$el.html(this.options.template(this.model)); 60 | return this; 61 | } 62 | 63 | }); 64 | 65 | fb.views.Error = Backbone.View.extend({ 66 | 67 | initialize: function () { 68 | this.template = _.template(fb.templateLoader.get('error')); 69 | this.render(); 70 | }, 71 | 72 | render: function () { 73 | this.$el.html(this.template()); 74 | return this; 75 | }, 76 | 77 | events: { 78 | 'click .retry':'retry' 79 | }, 80 | 81 | retry: function () { 82 | Backbone.history.loadUrl(Backbone.history.fragment); 83 | } 84 | 85 | }); 86 | 87 | fb.views.Feed = Backbone.View.extend({ 88 | 89 | initialize: function () { 90 | this.render(); 91 | }, 92 | 93 | render: function () { 94 | this.$el.html(this.options.template(this.model)); 95 | return this; 96 | } 97 | 98 | }); 99 | 100 | fb.views.Post = Backbone.View.extend({ 101 | 102 | initialize: function () { 103 | this.render(); 104 | }, 105 | 106 | render: function () { 107 | this.$el.html(this.options.template()); 108 | return this; 109 | }, 110 | 111 | events: { 112 | "click .post": "postMessage" 113 | }, 114 | 115 | postMessage: function () { 116 | var status = { 117 | name: "Check Out Sociogram Mobile", 118 | link: "http://coenraets.org", 119 | picture: "http://coenraets.org/sociogram/img/sociogram_80x86.png", 120 | caption: "A PhoneGap/Facebook starter app", 121 | description: "Sociogram is a sample application that demonstrates how to use the Facebook JavaScript SDK and the Graph API", 122 | message: $('.message').val() 123 | }; 124 | fb.spinner.show(); 125 | console.log(status); 126 | FB.api('/me/feed', 'post', status, function(response) { 127 | console.log(response); 128 | fb.spinner.hide(); 129 | if (response && response.id) { 130 | fb.alert('Your post was published.'); 131 | } else { 132 | fb.alert('Your post was not published.'); 133 | } 134 | }); 135 | return false; 136 | } 137 | 138 | }); 139 | 140 | fb.views.PostUI = Backbone.View.extend({ 141 | 142 | initialize: function () { 143 | this.render(); 144 | }, 145 | 146 | render: function () { 147 | this.$el.html(this.options.template()); 148 | return this; 149 | }, 150 | 151 | events: { 152 | "click .post": "postMessage" 153 | }, 154 | 155 | postMessage: function () { 156 | FB.ui( 157 | { 158 | method:'feed', 159 | name:'Check Out Sociogram Mobile', 160 | link: "http://coenraets.org", 161 | picture: "http://coenraets.org/sociogram/img/sociogram_80x86.png", 162 | caption: "A PhoneGap/Facebook starter app", 163 | description: "Sociogram is a sample application that demonstrates how to use the Facebook JavaScript SDK and the Graph API" 164 | }, 165 | function (response) { 166 | console.log(response); 167 | if (response && response.post_id) { 168 | fb.alert('Your post was published.'); 169 | } else { 170 | fb.alert('Your post was not published.'); 171 | } 172 | } 173 | ); 174 | return false; 175 | } 176 | 177 | }); 178 | 179 | fb.views.Revoke = Backbone.View.extend({ 180 | 181 | initialize: function () { 182 | this.render(); 183 | }, 184 | 185 | render: function () { 186 | this.$el.html(this.options.template()); 187 | return this; 188 | }, 189 | 190 | events: { 191 | "click .revoke": "revoke" 192 | }, 193 | 194 | revoke: function () { 195 | fb.spinner.show(); 196 | FB.api("/me/permissions", "delete", function () { 197 | fb.spinner.hide(); 198 | fb.alert('Permissions revoked'); 199 | FB.getLoginStatus(); 200 | }); 201 | $(document).trigger('permissions_revoved'); 202 | return false; 203 | } 204 | 205 | }); -------------------------------------------------------------------------------- /js/app.js: -------------------------------------------------------------------------------- 1 | window.addEventListener('load', function () { 2 | new FastClick(document.body); 3 | }, false); 4 | 5 | var fb = new MobileApp(); 6 | 7 | fb.spinner = $("#spinner"); 8 | fb.spinner.hide(); 9 | 10 | fb.slider = new PageSlider($('#container')); 11 | 12 | fb.MobileRouter = Backbone.Router.extend({ 13 | 14 | routes: { 15 | "": "welcome", 16 | "me": "me", 17 | "menu": "menu", 18 | "me/friends": "myfriends", 19 | "person/:id": "person", 20 | "person/:id/friends": "friends", 21 | "person/:id/mutualfriends": "mutualfriends", 22 | "me/feed": "myfeed", 23 | "person/:id/feed": "feed", 24 | "revoke": "revoke", 25 | "post": "post", 26 | "postui": "postui" 27 | }, 28 | 29 | welcome: function () { 30 | // Reset cached views 31 | fb.myView = null; 32 | fb.myFriendsView = null; 33 | var view = new fb.views.Welcome(); 34 | fb.slider.slidePageFrom(view.$el, "left"); 35 | }, 36 | 37 | menu: function () { 38 | fb.slider.slidePageFrom(new fb.views.Menu().$el, "left"); 39 | fb.slider.resetHistory(); 40 | }, 41 | 42 | me: function () { 43 | var self = this; 44 | if (fb.myView) { 45 | fb.slider.slidePage(fb.myView.$el); 46 | return; 47 | } 48 | fb.myView = new fb.views.Person({template: fb.templateLoader.get('person')}); 49 | var slide = fb.slider.slidePage(fb.myView.$el).done(function(){ 50 | fb.spinner.show(); 51 | }); 52 | var call = fbWrapper.api("/me"); 53 | $.when(slide, call) 54 | .done(function(slideResp, callResp) { 55 | fb.myView.model = callResp; 56 | fb.myView.render(); 57 | }) 58 | .fail(function() { 59 | self.showErrorPage(); 60 | }) 61 | .always(function() { 62 | fb.spinner.hide(); 63 | }); 64 | }, 65 | 66 | person: function (id) { 67 | var self = this; 68 | var view = new fb.views.Person({template: fb.templateLoader.get('person')}); 69 | var slide = fb.slider.slidePage(view.$el).done(function(){ 70 | fb.spinner.show(); 71 | }); 72 | var call = fbWrapper.api("/" + id); 73 | $.when(slide, call) 74 | .done(function(slideResp, callResp) { 75 | view.model = callResp; 76 | view.render(); 77 | }) 78 | .fail(function() { 79 | self.showErrorPage(); 80 | }) 81 | .always(function() { 82 | fb.spinner.hide(); 83 | }); 84 | }, 85 | 86 | myfriends: function () { 87 | var self = this; 88 | if (fb.myFriendsView) { 89 | fb.slider.slidePage(fb.myFriendsView.$el); 90 | return; 91 | } 92 | fb.myFriendsView = new fb.views.Friends({template: fb.templateLoader.get('friends')}); 93 | var slide = fb.slider.slidePage(fb.myFriendsView.$el).done(function() { 94 | fb.spinner.show(); 95 | }); 96 | var call = fbWrapper.api("/me/friends?limit=100"); 97 | $.when(slide, call) 98 | .done(function(slideResp, callResp) { 99 | fb.myFriendsView.model = callResp.data; 100 | fb.myFriendsView.render(); 101 | }) 102 | .fail(function() { 103 | self.showErrorPage(); 104 | }) 105 | .always(function() { 106 | fb.spinner.hide(); 107 | }); 108 | }, 109 | 110 | friends: function (id) { 111 | var self = this; 112 | var view = new fb.views.Friends({template: fb.templateLoader.get('friends')}); 113 | var slide = fb.slider.slidePage(view.$el).done(function() { 114 | fb.spinner.show(); 115 | }); 116 | var call = fbWrapper.api("/" + id + "/friends?limit=100"); 117 | $.when(slide, call) 118 | .done(function(slideResp, callResp) { 119 | view.model = callResp.data; 120 | view.render(); 121 | }) 122 | .fail(function() { 123 | self.showErrorPage(); 124 | }) 125 | .always(function() { 126 | fb.spinner.hide(); 127 | }); 128 | }, 129 | 130 | mutualfriends: function (id) { 131 | var self = this; 132 | var view = new fb.views.Friends({template: fb.templateLoader.get('friends')}); 133 | var slide = fb.slider.slidePage(view.$el).done(function() { 134 | fb.spinner.show(); 135 | }); 136 | var call = fbWrapper.api("/" + id + "/mutualfriends?limit=100"); 137 | $.when(slide, call) 138 | .done(function(slideResp, callResp) { 139 | view.model = callResp.data; 140 | view.render(); 141 | }) 142 | .fail(function() { 143 | self.showErrorPage(); 144 | }) 145 | .always(function() { 146 | fb.spinner.hide(); 147 | }); 148 | }, 149 | 150 | myfeed: function (id) { 151 | var self = this; 152 | var view = new fb.views.Feed({template: fb.templateLoader.get('feed')}); 153 | var slide = fb.slider.slidePage(view.$el).done(function() { 154 | fb.spinner.show(); 155 | }); 156 | var call = fbWrapper.api("/me/feed?limit=20"); 157 | $.when(slide, call) 158 | .done(function(slideResp, callResp) { 159 | view.model = callResp.data; 160 | view.render(); 161 | }) 162 | .fail(function() { 163 | self.showErrorPage(); 164 | }) 165 | .always(function() { 166 | fb.spinner.hide(); 167 | }); 168 | }, 169 | 170 | feed: function (id) { 171 | var self = this; 172 | var view = new fb.views.Feed({template: fb.templateLoader.get('feed')}); 173 | var slide = fb.slider.slidePage(view.$el).done(function() { 174 | fb.spinner.show(); 175 | }); 176 | var call = fbWrapper.api("/" + id + "/feed?limit=20"); 177 | $.when(slide, call) 178 | .done(function(slideResp, callResp) { 179 | view.model = callResp.data; 180 | view.render(); 181 | }) 182 | .fail(function() { 183 | self.showErrorPage(); 184 | }) 185 | .always(function() { 186 | fb.spinner.hide(); 187 | }); 188 | }, 189 | 190 | post: function () { 191 | fb.slider.slidePage(new fb.views.Post({template: fb.templateLoader.get("post")}).$el); 192 | }, 193 | 194 | postui: function () { 195 | fb.slider.slidePage(new fb.views.PostUI({template: fb.templateLoader.get("postui")}).$el); 196 | }, 197 | 198 | revoke: function () { 199 | fb.slider.slidePage(new fb.views.Revoke({template: fb.templateLoader.get("revoke")}).$el); 200 | }, 201 | 202 | showErrorPage: function () { 203 | fb.slider.slidePage(new fb.views.Error().$el); 204 | } 205 | 206 | }); 207 | 208 | $(document).on('ready', function () { 209 | 210 | fb.templateLoader.load(['menu', 'welcome', 'login', 'person', 'friends', 'feed', 'post', 'postui', 'error', 'revoke'], function () { 211 | fb.router = new fb.MobileRouter(); 212 | Backbone.history.start(); 213 | FB.init({ appId: "465374093524857", nativeInterface: CDV.FB, useCachedDialogs: false, status: true }); 214 | }); 215 | 216 | FB.Event.subscribe('auth.statusChange', function(event) { 217 | if (event.status === 'connected') { 218 | FB.api('/me', function (response) { 219 | fb.user = response; // Store the newly authenticated FB user 220 | }); 221 | fb.slider.removeCurrentPage(); 222 | fb.router.navigate("menu", {trigger: true}); 223 | } else { 224 | fb.user = null; // Reset current FB user 225 | fb.router.navigate("", {trigger: true}); 226 | } 227 | }); 228 | 229 | }); 230 | 231 | $(document).on('click', '.button.back', function() { 232 | window.history.back(); 233 | return false; 234 | }); 235 | 236 | $(document).on('click', '.logout', function () { 237 | FB.logout(); 238 | return false; 239 | }); 240 | 241 | $(document).on('login', function () { 242 | FB.login(function(response) { 243 | console.log("Logged In"); 244 | }, {scope: 'publish_actions,user_status,friends_status,read_stream'}); 245 | return false; 246 | }); 247 | 248 | $(document).on('permissions_revoked', function () { 249 | // Reset cached views 250 | fb.myView = null; 251 | fb.myFriendsView = null; 252 | return false; 253 | }); -------------------------------------------------------------------------------- /lib/underscore-min.js: -------------------------------------------------------------------------------- 1 | (function(){var n=this,t=n._,r={},e=Array.prototype,u=Object.prototype,i=Function.prototype,a=e.push,o=e.slice,c=e.concat,l=u.toString,f=u.hasOwnProperty,s=e.forEach,p=e.map,h=e.reduce,v=e.reduceRight,d=e.filter,g=e.every,m=e.some,y=e.indexOf,b=e.lastIndexOf,x=Array.isArray,_=Object.keys,j=i.bind,w=function(n){return n instanceof w?n:this instanceof w?(this._wrapped=n,void 0):new w(n)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=w),exports._=w):n._=w,w.VERSION="1.4.4";var A=w.each=w.forEach=function(n,t,e){if(null!=n)if(s&&n.forEach===s)n.forEach(t,e);else if(n.length===+n.length){for(var u=0,i=n.length;i>u;u++)if(t.call(e,n[u],u,n)===r)return}else for(var a in n)if(w.has(n,a)&&t.call(e,n[a],a,n)===r)return};w.map=w.collect=function(n,t,r){var e=[];return null==n?e:p&&n.map===p?n.map(t,r):(A(n,function(n,u,i){e[e.length]=t.call(r,n,u,i)}),e)};var O="Reduce of empty array with no initial value";w.reduce=w.foldl=w.inject=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),h&&n.reduce===h)return e&&(t=w.bind(t,e)),u?n.reduce(t,r):n.reduce(t);if(A(n,function(n,i,a){u?r=t.call(e,r,n,i,a):(r=n,u=!0)}),!u)throw new TypeError(O);return r},w.reduceRight=w.foldr=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),v&&n.reduceRight===v)return e&&(t=w.bind(t,e)),u?n.reduceRight(t,r):n.reduceRight(t);var i=n.length;if(i!==+i){var a=w.keys(n);i=a.length}if(A(n,function(o,c,l){c=a?a[--i]:--i,u?r=t.call(e,r,n[c],c,l):(r=n[c],u=!0)}),!u)throw new TypeError(O);return r},w.find=w.detect=function(n,t,r){var e;return E(n,function(n,u,i){return t.call(r,n,u,i)?(e=n,!0):void 0}),e},w.filter=w.select=function(n,t,r){var e=[];return null==n?e:d&&n.filter===d?n.filter(t,r):(A(n,function(n,u,i){t.call(r,n,u,i)&&(e[e.length]=n)}),e)},w.reject=function(n,t,r){return w.filter(n,function(n,e,u){return!t.call(r,n,e,u)},r)},w.every=w.all=function(n,t,e){t||(t=w.identity);var u=!0;return null==n?u:g&&n.every===g?n.every(t,e):(A(n,function(n,i,a){return(u=u&&t.call(e,n,i,a))?void 0:r}),!!u)};var E=w.some=w.any=function(n,t,e){t||(t=w.identity);var u=!1;return null==n?u:m&&n.some===m?n.some(t,e):(A(n,function(n,i,a){return u||(u=t.call(e,n,i,a))?r:void 0}),!!u)};w.contains=w.include=function(n,t){return null==n?!1:y&&n.indexOf===y?n.indexOf(t)!=-1:E(n,function(n){return n===t})},w.invoke=function(n,t){var r=o.call(arguments,2),e=w.isFunction(t);return w.map(n,function(n){return(e?t:n[t]).apply(n,r)})},w.pluck=function(n,t){return w.map(n,function(n){return n[t]})},w.where=function(n,t,r){return w.isEmpty(t)?r?null:[]:w[r?"find":"filter"](n,function(n){for(var r in t)if(t[r]!==n[r])return!1;return!0})},w.findWhere=function(n,t){return w.where(n,t,!0)},w.max=function(n,t,r){if(!t&&w.isArray(n)&&n[0]===+n[0]&&65535>n.length)return Math.max.apply(Math,n);if(!t&&w.isEmpty(n))return-1/0;var e={computed:-1/0,value:-1/0};return A(n,function(n,u,i){var a=t?t.call(r,n,u,i):n;a>=e.computed&&(e={value:n,computed:a})}),e.value},w.min=function(n,t,r){if(!t&&w.isArray(n)&&n[0]===+n[0]&&65535>n.length)return Math.min.apply(Math,n);if(!t&&w.isEmpty(n))return 1/0;var e={computed:1/0,value:1/0};return A(n,function(n,u,i){var a=t?t.call(r,n,u,i):n;e.computed>a&&(e={value:n,computed:a})}),e.value},w.shuffle=function(n){var t,r=0,e=[];return A(n,function(n){t=w.random(r++),e[r-1]=e[t],e[t]=n}),e};var k=function(n){return w.isFunction(n)?n:function(t){return t[n]}};w.sortBy=function(n,t,r){var e=k(t);return w.pluck(w.map(n,function(n,t,u){return{value:n,index:t,criteria:e.call(r,n,t,u)}}).sort(function(n,t){var r=n.criteria,e=t.criteria;if(r!==e){if(r>e||r===void 0)return 1;if(e>r||e===void 0)return-1}return n.indexi;){var o=i+a>>>1;u>r.call(e,n[o])?i=o+1:a=o}return i},w.toArray=function(n){return n?w.isArray(n)?o.call(n):n.length===+n.length?w.map(n,w.identity):w.values(n):[]},w.size=function(n){return null==n?0:n.length===+n.length?n.length:w.keys(n).length},w.first=w.head=w.take=function(n,t,r){return null==n?void 0:null==t||r?n[0]:o.call(n,0,t)},w.initial=function(n,t,r){return o.call(n,0,n.length-(null==t||r?1:t))},w.last=function(n,t,r){return null==n?void 0:null==t||r?n[n.length-1]:o.call(n,Math.max(n.length-t,0))},w.rest=w.tail=w.drop=function(n,t,r){return o.call(n,null==t||r?1:t)},w.compact=function(n){return w.filter(n,w.identity)};var R=function(n,t,r){return A(n,function(n){w.isArray(n)?t?a.apply(r,n):R(n,t,r):r.push(n)}),r};w.flatten=function(n,t){return R(n,t,[])},w.without=function(n){return w.difference(n,o.call(arguments,1))},w.uniq=w.unique=function(n,t,r,e){w.isFunction(t)&&(e=r,r=t,t=!1);var u=r?w.map(n,r,e):n,i=[],a=[];return A(u,function(r,e){(t?e&&a[a.length-1]===r:w.contains(a,r))||(a.push(r),i.push(n[e]))}),i},w.union=function(){return w.uniq(c.apply(e,arguments))},w.intersection=function(n){var t=o.call(arguments,1);return w.filter(w.uniq(n),function(n){return w.every(t,function(t){return w.indexOf(t,n)>=0})})},w.difference=function(n){var t=c.apply(e,o.call(arguments,1));return w.filter(n,function(n){return!w.contains(t,n)})},w.zip=function(){for(var n=o.call(arguments),t=w.max(w.pluck(n,"length")),r=Array(t),e=0;t>e;e++)r[e]=w.pluck(n,""+e);return r},w.object=function(n,t){if(null==n)return{};for(var r={},e=0,u=n.length;u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},w.indexOf=function(n,t,r){if(null==n)return-1;var e=0,u=n.length;if(r){if("number"!=typeof r)return e=w.sortedIndex(n,t),n[e]===t?e:-1;e=0>r?Math.max(0,u+r):r}if(y&&n.indexOf===y)return n.indexOf(t,r);for(;u>e;e++)if(n[e]===t)return e;return-1},w.lastIndexOf=function(n,t,r){if(null==n)return-1;var e=null!=r;if(b&&n.lastIndexOf===b)return e?n.lastIndexOf(t,r):n.lastIndexOf(t);for(var u=e?r:n.length;u--;)if(n[u]===t)return u;return-1},w.range=function(n,t,r){1>=arguments.length&&(t=n||0,n=0),r=arguments[2]||1;for(var e=Math.max(Math.ceil((t-n)/r),0),u=0,i=Array(e);e>u;)i[u++]=n,n+=r;return i},w.bind=function(n,t){if(n.bind===j&&j)return j.apply(n,o.call(arguments,1));var r=o.call(arguments,2);return function(){return n.apply(t,r.concat(o.call(arguments)))}},w.partial=function(n){var t=o.call(arguments,1);return function(){return n.apply(this,t.concat(o.call(arguments)))}},w.bindAll=function(n){var t=o.call(arguments,1);return 0===t.length&&(t=w.functions(n)),A(t,function(t){n[t]=w.bind(n[t],n)}),n},w.memoize=function(n,t){var r={};return t||(t=w.identity),function(){var e=t.apply(this,arguments);return w.has(r,e)?r[e]:r[e]=n.apply(this,arguments)}},w.delay=function(n,t){var r=o.call(arguments,2);return setTimeout(function(){return n.apply(null,r)},t)},w.defer=function(n){return w.delay.apply(w,[n,1].concat(o.call(arguments,1)))},w.throttle=function(n,t){var r,e,u,i,a=0,o=function(){a=new Date,u=null,i=n.apply(r,e)};return function(){var c=new Date,l=t-(c-a);return r=this,e=arguments,0>=l?(clearTimeout(u),u=null,a=c,i=n.apply(r,e)):u||(u=setTimeout(o,l)),i}},w.debounce=function(n,t,r){var e,u;return function(){var i=this,a=arguments,o=function(){e=null,r||(u=n.apply(i,a))},c=r&&!e;return clearTimeout(e),e=setTimeout(o,t),c&&(u=n.apply(i,a)),u}},w.once=function(n){var t,r=!1;return function(){return r?t:(r=!0,t=n.apply(this,arguments),n=null,t)}},w.wrap=function(n,t){return function(){var r=[n];return a.apply(r,arguments),t.apply(this,r)}},w.compose=function(){var n=arguments;return function(){for(var t=arguments,r=n.length-1;r>=0;r--)t=[n[r].apply(this,t)];return t[0]}},w.after=function(n,t){return 0>=n?t():function(){return 1>--n?t.apply(this,arguments):void 0}},w.keys=_||function(n){if(n!==Object(n))throw new TypeError("Invalid object");var t=[];for(var r in n)w.has(n,r)&&(t[t.length]=r);return t},w.values=function(n){var t=[];for(var r in n)w.has(n,r)&&t.push(n[r]);return t},w.pairs=function(n){var t=[];for(var r in n)w.has(n,r)&&t.push([r,n[r]]);return t},w.invert=function(n){var t={};for(var r in n)w.has(n,r)&&(t[n[r]]=r);return t},w.functions=w.methods=function(n){var t=[];for(var r in n)w.isFunction(n[r])&&t.push(r);return t.sort()},w.extend=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)n[r]=t[r]}),n},w.pick=function(n){var t={},r=c.apply(e,o.call(arguments,1));return A(r,function(r){r in n&&(t[r]=n[r])}),t},w.omit=function(n){var t={},r=c.apply(e,o.call(arguments,1));for(var u in n)w.contains(r,u)||(t[u]=n[u]);return t},w.defaults=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)null==n[r]&&(n[r]=t[r])}),n},w.clone=function(n){return w.isObject(n)?w.isArray(n)?n.slice():w.extend({},n):n},w.tap=function(n,t){return t(n),n};var I=function(n,t,r,e){if(n===t)return 0!==n||1/n==1/t;if(null==n||null==t)return n===t;n instanceof w&&(n=n._wrapped),t instanceof w&&(t=t._wrapped);var u=l.call(n);if(u!=l.call(t))return!1;switch(u){case"[object String]":return n==t+"";case"[object Number]":return n!=+n?t!=+t:0==n?1/n==1/t:n==+t;case"[object Date]":case"[object Boolean]":return+n==+t;case"[object RegExp]":return n.source==t.source&&n.global==t.global&&n.multiline==t.multiline&&n.ignoreCase==t.ignoreCase}if("object"!=typeof n||"object"!=typeof t)return!1;for(var i=r.length;i--;)if(r[i]==n)return e[i]==t;r.push(n),e.push(t);var a=0,o=!0;if("[object Array]"==u){if(a=n.length,o=a==t.length)for(;a--&&(o=I(n[a],t[a],r,e)););}else{var c=n.constructor,f=t.constructor;if(c!==f&&!(w.isFunction(c)&&c instanceof c&&w.isFunction(f)&&f instanceof f))return!1;for(var s in n)if(w.has(n,s)&&(a++,!(o=w.has(t,s)&&I(n[s],t[s],r,e))))break;if(o){for(s in t)if(w.has(t,s)&&!a--)break;o=!a}}return r.pop(),e.pop(),o};w.isEqual=function(n,t){return I(n,t,[],[])},w.isEmpty=function(n){if(null==n)return!0;if(w.isArray(n)||w.isString(n))return 0===n.length;for(var t in n)if(w.has(n,t))return!1;return!0},w.isElement=function(n){return!(!n||1!==n.nodeType)},w.isArray=x||function(n){return"[object Array]"==l.call(n)},w.isObject=function(n){return n===Object(n)},A(["Arguments","Function","String","Number","Date","RegExp"],function(n){w["is"+n]=function(t){return l.call(t)=="[object "+n+"]"}}),w.isArguments(arguments)||(w.isArguments=function(n){return!(!n||!w.has(n,"callee"))}),"function"!=typeof/./&&(w.isFunction=function(n){return"function"==typeof n}),w.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))},w.isNaN=function(n){return w.isNumber(n)&&n!=+n},w.isBoolean=function(n){return n===!0||n===!1||"[object Boolean]"==l.call(n)},w.isNull=function(n){return null===n},w.isUndefined=function(n){return n===void 0},w.has=function(n,t){return f.call(n,t)},w.noConflict=function(){return n._=t,this},w.identity=function(n){return n},w.times=function(n,t,r){for(var e=Array(n),u=0;n>u;u++)e[u]=t.call(r,u);return e},w.random=function(n,t){return null==t&&(t=n,n=0),n+Math.floor(Math.random()*(t-n+1))};var M={escape:{"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"}};M.unescape=w.invert(M.escape);var S={escape:RegExp("["+w.keys(M.escape).join("")+"]","g"),unescape:RegExp("("+w.keys(M.unescape).join("|")+")","g")};w.each(["escape","unescape"],function(n){w[n]=function(t){return null==t?"":(""+t).replace(S[n],function(t){return M[n][t]})}}),w.result=function(n,t){if(null==n)return null;var r=n[t];return w.isFunction(r)?r.call(n):r},w.mixin=function(n){A(w.functions(n),function(t){var r=w[t]=n[t];w.prototype[t]=function(){var n=[this._wrapped];return a.apply(n,arguments),D.call(this,r.apply(w,n))}})};var N=0;w.uniqueId=function(n){var t=++N+"";return n?n+t:t},w.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var T=/(.)^/,q={"'":"'","\\":"\\","\r":"r","\n":"n"," ":"t","\u2028":"u2028","\u2029":"u2029"},B=/\\|'|\r|\n|\t|\u2028|\u2029/g;w.template=function(n,t,r){var e;r=w.defaults({},r,w.templateSettings);var u=RegExp([(r.escape||T).source,(r.interpolate||T).source,(r.evaluate||T).source].join("|")+"|$","g"),i=0,a="__p+='";n.replace(u,function(t,r,e,u,o){return a+=n.slice(i,o).replace(B,function(n){return"\\"+q[n]}),r&&(a+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'"),e&&(a+="'+\n((__t=("+e+"))==null?'':__t)+\n'"),u&&(a+="';\n"+u+"\n__p+='"),i=o+t.length,t}),a+="';\n",r.variable||(a="with(obj||{}){\n"+a+"}\n"),a="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+a+"return __p;\n";try{e=Function(r.variable||"obj","_",a)}catch(o){throw o.source=a,o}if(t)return e(t,w);var c=function(n){return e.call(this,n,w)};return c.source="function("+(r.variable||"obj")+"){\n"+a+"}",c},w.chain=function(n){return w(n).chain()};var D=function(n){return this._chain?w(n).chain():n};w.mixin(w),A(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=e[n];w.prototype[n]=function(){var r=this._wrapped;return t.apply(r,arguments),"shift"!=n&&"splice"!=n||0!==r.length||delete r[0],D.call(this,r)}}),A(["concat","join","slice"],function(n){var t=e[n];w.prototype[n]=function(){return D.call(this,t.apply(this._wrapped,arguments))}}),w.extend(w.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}})}).call(this); -------------------------------------------------------------------------------- /lib/backbone-min.js: -------------------------------------------------------------------------------- 1 | // Backbone.js 0.9.10 2 | 3 | // (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc. 4 | // Backbone may be freely distributed under the MIT license. 5 | // For all details and documentation: 6 | // http://backbonejs.org 7 | (function(){var n=this,B=n.Backbone,h=[],C=h.push,u=h.slice,D=h.splice,g;g="undefined"!==typeof exports?exports:n.Backbone={};g.VERSION="0.9.10";var f=n._;!f&&"undefined"!==typeof require&&(f=require("underscore"));g.$=n.jQuery||n.Zepto||n.ender;g.noConflict=function(){n.Backbone=B;return this};g.emulateHTTP=!1;g.emulateJSON=!1;var v=/\s+/,q=function(a,b,c,d){if(!c)return!0;if("object"===typeof c)for(var e in c)a[b].apply(a,[e,c[e]].concat(d));else if(v.test(c)){c=c.split(v);e=0;for(var f=c.length;e< 8 | f;e++)a[b].apply(a,[c[e]].concat(d))}else return!0},w=function(a,b){var c,d=-1,e=a.length;switch(b.length){case 0:for(;++d=b);this.root=("/"+this.root+"/").replace(I,"/");b&&this._wantsHashChange&&(this.iframe=g.$('