+ <%= render 'users/contributions', :paginate_params => {:controller => 'users', :action => 'contributions'} %>
Activity Placeholder
diff --git a/config/locales/en/geo_data.yml b/config/locales/en/geo_data.yml
index 2c03f10..e5dc624 100644
--- a/config/locales/en/geo_data.yml
+++ b/config/locales/en/geo_data.yml
@@ -16,5 +16,8 @@ en:
add_to_map: "Add to a Map"
+ list:
+ title: '%{count} objects'
+
flash:
updated: 'Data successfully updated'
diff --git a/config/locales/pt-BR/geo_data.yml b/config/locales/pt-BR/geo_data.yml
index f9f5979..4553dd0 100644
--- a/config/locales/pt-BR/geo_data.yml
+++ b/config/locales/pt-BR/geo_data.yml
@@ -16,5 +16,8 @@ pt-BR:
add_to_map: "Adicone a um Mapa"
+ list:
+ title: '%{count} objetos'
+
flash:
updated: 'Dados atualizados com sucesso'
diff --git a/config/routes.rb b/config/routes.rb
index 48ab700..22feaac 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -14,7 +14,11 @@
get "tags/search" => "tags#search", :as => :tag_search
resources :users do
- member { get :activate }
+ member do
+ get :activate
+ get :contributions
+ end
+
collection do
get :created
@@ -25,7 +29,7 @@
end
end
- resources :geo_data, :only => [:show, :edit, :update]
+ resources :geo_data, :only => [:index, :show, :edit, :update]
if Rails.env.development?
mount LetterOpenerWeb::Engine, :at => "/letter_opener"
diff --git a/db/seeds/development/geo_data.seeds.rb b/db/seeds/development/geo_data.seeds.rb
index 7ef15b7..6c9ad79 100644
--- a/db/seeds/development/geo_data.seeds.rb
+++ b/db/seeds/development/geo_data.seeds.rb
@@ -7,6 +7,13 @@ def geo_data_fixture
data.contacts = {"city" => "São Paulo", "site" => "br.okfn.br", "phone" => "98765456789", "address" => "Av Paulista, 42"}
data.tags = ["open data", "ngo", "hacking"]
data.save
+
+ data = GeoData.find_or_create_by(name: "OKFN-BR - Open Knowledge Foundation Brazil")
+ data.description = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Donec et mollis dolor. Praesent et diam eget libero egestas mattis sit amet vitae augue. Nam tincidunt congue enim, ut porta lorem lacinia consectetur. Donec ut libero sed arcu vehicula ultricies a non tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ut gravida lorem. Ut turpis felis, pulvinar a semper sed, adipiscing id dolor. Pellentesque auctor nisi id magna consequat sagittis. Curabitur dapibus enim sit amet elit pharetra tincidunt feugiat nisl imperdiet. Ut convallis libero in urna ultrices accumsan. Donec sed odio eros. Donec viverra mi quis quam pulvinar at malesuada arcu rhoncus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In rutrum accumsan ultricies. Mauris vitae nisi at sem facilisis semper ac in est."
+ data.location = "GEOMETRYCOLLECTION (POINT (-46.984319110450746 -23.763487791267138))"
+ data.contacts = {"city" => "São Paulo", "site" => "br.okfn.br", "phone" => "98765456789", "address" => "Av Paulista, 42"}
+ data.tags = ["open data", "ngo", "hacking"]
+ data.save
end
end
diff --git a/docs/components/list.md b/docs/components/list.md
new file mode 100644
index 0000000..f19d5d8
--- /dev/null
+++ b/docs/components/list.md
@@ -0,0 +1,19 @@
+## List
+
+list of objects using infinite scroll (uses infinite-scroll)
+
+### Markup
+
+```
+
+ <%= paginate @collection, :remote => true %>
+
+ <%= render @collection %>
+
+
+```
+
+### Options
+```
+:scroll => default undefined # infinite-scroll options
+```
diff --git a/spec/controllers/geo_data_controller_spec.rb b/spec/controllers/geo_data_controller_spec.rb
index 85ecb86..048519a 100644
--- a/spec/controllers/geo_data_controller_spec.rb
+++ b/spec/controllers/geo_data_controller_spec.rb
@@ -1,6 +1,23 @@
require 'spec_helper'
describe GeoDataController do
+ describe "GET index" do
+ context "regular request" do
+ it 'renders geo_data list' do
+ get :index
+ expect(response).to render_template :index
+ end
+ end
+ context "xhr" do
+ before { controller.request.stub(:xhr?).and_return(true) }
+
+ it 'renders geo_data list without layout' do
+ get :index
+ expect(response).to render_template(:layout => nil)
+ end
+ end
+ end
+
describe "GET show" do
let(:data) { FactoryGirl.create :geo_data }
it 'calls find_data filter' do
diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb
index e8639a1..effccc0 100644
--- a/spec/controllers/users_controller_spec.rb
+++ b/spec/controllers/users_controller_spec.rb
@@ -237,4 +237,24 @@
end
end
+ describe "GET contributions" do
+ let(:user) { FactoryGirl.create :user }
+
+ context "regular request" do
+ it 'renders show' do
+ get :contributions, {id: user.id}
+ expect(response).to render_template :contributions
+ end
+ end
+
+ context "xhr" do
+ before { controller.request.stub(:xhr?).and_return(true) }
+
+ it 'renders contributions without layout' do
+ get :contributions, {id: user.id}
+ expect(response).to render_template(:layout => nil)
+ end
+ end
+ end
+
end
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index a473198..3bb3d36 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -127,6 +127,13 @@
it { expect(helper.counters_list(obj, only=[:map]).size).to eq 1 }
end
+ describe "#object_type" do
+ let(:user) { FactoryGirl.create :user }
+ let(:geo_data) { FactoryGirl.create :geo_data }
+ it { expect(helper.object_type user).to eq :user }
+ it { expect(helper.object_type geo_data).to eq :data }
+ it { expect(helper.object_type nil).to eq :unknown }
+ end
describe "Corcerns::ContactsHelper" do
let(:user) { FactoryGirl.create :user, :contacts => {'address' => 'rua Bla', 'phone' => '12345'} }
diff --git a/spec/javascripts/components/list_spec.js.coffee b/spec/javascripts/components/list_spec.js.coffee
new file mode 100644
index 0000000..8ff0eb9
--- /dev/null
+++ b/spec/javascripts/components/list_spec.js.coffee
@@ -0,0 +1,21 @@
+#= require spec_helper
+
+describe "list", ->
+ beforeEach ->
+ @container = $ JST['templates/list']()
+
+ it 'initializes component', ->
+ spy App.components, 'list', =>
+ App.mediator.publish 'components:start', @container
+ expect(App.components.list).to.be.called
+
+
+ describe 'component', ->
+ beforeEach ->
+ @container = $ JST['templates/list']()
+ @component = App.components.list @container
+
+ it 'calls infinite-scroll plugin', ->
+ spy @component.list, 'infinitescroll', =>
+ @component.init()
+ expect(@component.list.infinitescroll).to.be.calledWith @component.infiniteScrollDefaults
diff --git a/spec/javascripts/templates/list.jst.ejs b/spec/javascripts/templates/list.jst.ejs
new file mode 100644
index 0000000..3734db5
--- /dev/null
+++ b/spec/javascripts/templates/list.jst.ejs
@@ -0,0 +1,6 @@
+
diff --git a/vendor/assets/javascripts/jquery.infinitescroll.js b/vendor/assets/javascripts/jquery.infinitescroll.js
new file mode 100644
index 0000000..b05da56
--- /dev/null
+++ b/vendor/assets/javascripts/jquery.infinitescroll.js
@@ -0,0 +1,821 @@
+/*global jQuery: true */
+
+/*!
+ --------------------------------
+ Infinite Scroll
+ --------------------------------
+ + https://github.com/paulirish/infinite-scroll
+ + version 2.0.2
+ + Copyright 2011/12 Paul Irish & Luke Shumard
+ + Licensed under the MIT license
+
+ + Documentation: http://infinite-scroll.com/
+*/
+
+// Uses AMD or browser globals to create a jQuery plugin.
+(function (factory) {
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as an anonymous module.
+ define(['jquery'], factory);
+ } else {
+ // Browser globals
+ factory(jQuery);
+ }
+}(function ($, undefined) {
+ "use strict";
+
+ $.infinitescroll = function infscr(options, callback, element) {
+ this.element = $(element);
+
+ // Flag the object in the event of a failed creation
+ if (!this._create(options, callback)) {
+ this.failed = true;
+ }
+ };
+
+ $.infinitescroll.defaults = {
+ loading: {
+ finished: undefined,
+ finishedMsg: "
Congratulations, you've reached the end of the internet.",
+ img: "data:image/gif;base64,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",
+ msg: null,
+ msgText: "
Loading the next set of posts...",
+ selector: null,
+ speed: 'fast',
+ start: undefined
+ },
+ state: {
+ isDuringAjax: false,
+ isInvalidPage: false,
+ isDestroyed: false,
+ isDone: false, // For when it goes all the way through the archive.
+ isPaused: false,
+ isBeyondMaxPage: false,
+ currPage: 1
+ },
+ debug: false,
+ behavior: undefined,
+ binder: $(window), // used to cache the selector
+ nextSelector: "div.navigation a:first",
+ navSelector: "div.navigation",
+ contentSelector: null, // rename to pageFragment
+ extraScrollPx: 150,
+ itemSelector: "div.post",
+ animate: false,
+ pathParse: undefined,
+ dataType: 'html',
+ appendCallback: true,
+ bufferPx: 40,
+ errorCallback: function () { },
+ infid: 0, //Instance ID
+ pixelsFromNavToBottom: undefined,
+ path: undefined, // Either parts of a URL as an array (e.g. ["/page/", "/"] or a function that takes in the page number and returns a URL
+ prefill: false, // When the document is smaller than the window, load data until the document is larger or links are exhausted
+ maxPage: undefined // to manually control maximum page (when maxPage is undefined, maximum page limitation is not work)
+ };
+
+ $.infinitescroll.prototype = {
+
+ /*
+ ----------------------------
+ Private methods
+ ----------------------------
+ */
+
+ // Bind or unbind from scroll
+ _binding: function infscr_binding(binding) {
+
+ var instance = this,
+ opts = instance.options;
+
+ opts.v = '2.0b2.120520';
+
+ // if behavior is defined and this function is extended, call that instead of default
+ if (!!opts.behavior && this['_binding_'+opts.behavior] !== undefined) {
+ this['_binding_'+opts.behavior].call(this);
+ return;
+ }
+
+ if (binding !== 'bind' && binding !== 'unbind') {
+ this._debug('Binding value ' + binding + ' not valid');
+ return false;
+ }
+
+ if (binding === 'unbind') {
+ (this.options.binder).unbind('smartscroll.infscr.' + instance.options.infid);
+ } else {
+ (this.options.binder)[binding]('smartscroll.infscr.' + instance.options.infid, function () {
+ instance.scroll();
+ });
+ }
+
+ this._debug('Binding', binding);
+ },
+
+ // Fundamental aspects of the plugin are initialized
+ _create: function infscr_create(options, callback) {
+
+ // Add custom options to defaults
+ var opts = $.extend(true, {}, $.infinitescroll.defaults, options);
+ this.options = opts;
+ var $window = $(window);
+ var instance = this;
+
+ // Validate selectors
+ if (!instance._validate(options)) {
+ return false;
+ }
+
+ // Validate page fragment path
+ var path = $(opts.nextSelector).attr('href');
+ if (!path) {
+ this._debug('Navigation selector not found');
+ return false;
+ }
+
+ // Set the path to be a relative URL from root.
+ opts.path = opts.path || this._determinepath(path);
+
+ // contentSelector is 'page fragment' option for .load() / .ajax() calls
+ opts.contentSelector = opts.contentSelector || this.element;
+
+ // loading.selector - if we want to place the load message in a specific selector, defaulted to the contentSelector
+ opts.loading.selector = opts.loading.selector || opts.contentSelector;
+
+ // Define loading.msg
+ opts.loading.msg = opts.loading.msg || $('
' + opts.loading.msgText + '
');
+
+ // Preload loading.img
+ (new Image()).src = opts.loading.img;
+
+ // distance from nav links to bottom
+ // computed as: height of the document + top offset of container - top offset of nav link
+ if(opts.pixelsFromNavToBottom === undefined) {
+ opts.pixelsFromNavToBottom = $(document).height() - $(opts.navSelector).offset().top;
+ this._debug("pixelsFromNavToBottom: " + opts.pixelsFromNavToBottom);
+ }
+
+ var self = this;
+
+ // determine loading.start actions
+ opts.loading.start = opts.loading.start || function() {
+ $(opts.navSelector).hide();
+ opts.loading.msg
+ .appendTo(opts.loading.selector)
+ .show(opts.loading.speed, $.proxy(function() {
+ this.beginAjax(opts);
+ }, self));
+ };
+
+ // determine loading.finished actions
+ opts.loading.finished = opts.loading.finished || function() {
+ if (!opts.state.isBeyondMaxPage)
+ opts.loading.msg.fadeOut(opts.loading.speed);
+ };
+
+ // callback loading
+ opts.callback = function(instance, data, url) {
+ if (!!opts.behavior && instance['_callback_'+opts.behavior] !== undefined) {
+ instance['_callback_'+opts.behavior].call($(opts.contentSelector)[0], data, url);
+ }
+
+ if (callback) {
+ callback.call($(opts.contentSelector)[0], data, opts, url);
+ }
+
+ if (opts.prefill) {
+ $window.bind("resize.infinite-scroll", instance._prefill);
+ }
+ };
+
+ if (options.debug) {
+ // Tell IE9 to use its built-in console
+ if (Function.prototype.bind && (typeof console === 'object' || typeof console === 'function') && typeof console.log === "object") {
+ ["log","info","warn","error","assert","dir","clear","profile","profileEnd"]
+ .forEach(function (method) {
+ console[method] = this.call(console[method], console);
+ }, Function.prototype.bind);
+ }
+ }
+
+ this._setup();
+
+ // Setups the prefill method for use
+ if (opts.prefill) {
+ this._prefill();
+ }
+
+ // Return true to indicate successful creation
+ return true;
+ },
+
+ _prefill: function infscr_prefill() {
+ var instance = this;
+ var $window = $(window);
+
+ function needsPrefill() {
+ return (instance.options.contentSelector.height() <= $window.height());
+ }
+
+ this._prefill = function() {
+ if (needsPrefill()) {
+ instance.scroll();
+ }
+
+ $window.bind("resize.infinite-scroll", function() {
+ if (needsPrefill()) {
+ $window.unbind("resize.infinite-scroll");
+ instance.scroll();
+ }
+ });
+ };
+
+ // Call self after setting up the new function
+ this._prefill();
+ },
+
+ // Console log wrapper
+ _debug: function infscr_debug() {
+ if (true !== this.options.debug) {
+ return;
+ }
+
+ if (typeof console !== 'undefined' && typeof console.log === 'function') {
+ // Modern browsers
+ // Single argument, which is a string
+ if ((Array.prototype.slice.call(arguments)).length === 1 && typeof Array.prototype.slice.call(arguments)[0] === 'string') {
+ console.log( (Array.prototype.slice.call(arguments)).toString() );
+ } else {
+ console.log( Array.prototype.slice.call(arguments) );
+ }
+ } else if (!Function.prototype.bind && typeof console !== 'undefined' && typeof console.log === 'object') {
+ // IE8
+ Function.prototype.call.call(console.log, console, Array.prototype.slice.call(arguments));
+ }
+ },
+
+ // find the number to increment in the path.
+ _determinepath: function infscr_determinepath(path) {
+
+ var opts = this.options;
+
+ // if behavior is defined and this function is extended, call that instead of default
+ if (!!opts.behavior && this['_determinepath_'+opts.behavior] !== undefined) {
+ return this['_determinepath_'+opts.behavior].call(this,path);
+ }
+
+ if (!!opts.pathParse) {
+
+ this._debug('pathParse manual');
+ return opts.pathParse(path, this.options.state.currPage+1);
+
+ } else if (path.match(/^(.*?)\b2\b(.*?$)/)) {
+ path = path.match(/^(.*?)\b2\b(.*?$)/).slice(1);
+
+ // if there is any 2 in the url at all.
+ } else if (path.match(/^(.*?)2(.*?$)/)) {
+
+ // page= is used in django:
+ // http://www.infinite-scroll.com/changelog/comment-page-1/#comment-127
+ if (path.match(/^(.*?page=)2(\/.*|$)/)) {
+ path = path.match(/^(.*?page=)2(\/.*|$)/).slice(1);
+ return path;
+ }
+
+ path = path.match(/^(.*?)2(.*?$)/).slice(1);
+
+ } else {
+
+ // page= is used in drupal too but second page is page=1 not page=2:
+ // thx Jerod Fritz, vladikoff
+ if (path.match(/^(.*?page=)1(\/.*|$)/)) {
+ path = path.match(/^(.*?page=)1(\/.*|$)/).slice(1);
+ return path;
+ } else {
+ this._debug('Sorry, we couldn\'t parse your Next (Previous Posts) URL. Verify your the css selector points to the correct A tag. If you still get this error: yell, scream, and kindly ask for help at infinite-scroll.com.');
+ // Get rid of isInvalidPage to allow permalink to state
+ opts.state.isInvalidPage = true; //prevent it from running on this page.
+ }
+ }
+ this._debug('determinePath', path);
+ return path;
+
+ },
+
+ // Custom error
+ _error: function infscr_error(xhr) {
+
+ var opts = this.options;
+
+ // if behavior is defined and this function is extended, call that instead of default
+ if (!!opts.behavior && this['_error_'+opts.behavior] !== undefined) {
+ this['_error_'+opts.behavior].call(this,xhr);
+ return;
+ }
+
+ if (xhr !== 'destroy' && xhr !== 'end') {
+ xhr = 'unknown';
+ }
+
+ this._debug('Error', xhr);
+
+ if (xhr === 'end' || opts.state.isBeyondMaxPage) {
+ this._showdonemsg();
+ }
+
+ opts.state.isDone = true;
+ opts.state.currPage = 1; // if you need to go back to this instance
+ opts.state.isPaused = false;
+ opts.state.isBeyondMaxPage = false;
+ this._binding('unbind');
+
+ },
+
+ // Load Callback
+ _loadcallback: function infscr_loadcallback(box, data, url) {
+ var opts = this.options,
+ callback = this.options.callback, // GLOBAL OBJECT FOR CALLBACK
+ result = (opts.state.isDone) ? 'done' : (!opts.appendCallback) ? 'no-append' : 'append',
+ frag;
+
+ // if behavior is defined and this function is extended, call that instead of default
+ if (!!opts.behavior && this['_loadcallback_'+opts.behavior] !== undefined) {
+ this['_loadcallback_'+opts.behavior].call(this,box,data);
+ return;
+ }
+
+ switch (result) {
+ case 'done':
+ this._showdonemsg();
+ return false;
+
+ case 'no-append':
+ if (opts.dataType === 'html') {
+ data = '
' + data + '
';
+ data = $(data).find(opts.itemSelector);
+ }
+ break;
+
+ case 'append':
+ var children = box.children();
+ // if it didn't return anything
+ if (children.length === 0) {
+ return this._error('end');
+ }
+
+ // use a documentFragment because it works when content is going into a table or UL
+ frag = document.createDocumentFragment();
+ while (box[0].firstChild) {
+ frag.appendChild(box[0].firstChild);
+ }
+
+ this._debug('contentSelector', $(opts.contentSelector)[0]);
+ $(opts.contentSelector)[0].appendChild(frag);
+ // previously, we would pass in the new DOM element as context for the callback
+ // however we're now using a documentfragment, which doesn't have parents or children,
+ // so the context is the contentContainer guy, and we pass in an array
+ // of the elements collected as the first argument.
+
+ data = children.get();
+ break;
+ }
+
+ // loadingEnd function
+ opts.loading.finished.call($(opts.contentSelector)[0],opts);
+
+ // smooth scroll to ease in the new content
+ if (opts.animate) {
+ var scrollTo = $(window).scrollTop() + $(opts.loading.msg).height() + opts.extraScrollPx + 'px';
+ $('html,body').animate({ scrollTop: scrollTo }, 800, function () { opts.state.isDuringAjax = false; });
+ }
+
+ if (!opts.animate) {
+ // once the call is done, we can allow it again.
+ opts.state.isDuringAjax = false;
+ }
+
+ callback(this, data, url);
+
+ if (opts.prefill) {
+ this._prefill();
+ }
+ },
+
+ _nearbottom: function infscr_nearbottom() {
+
+ var opts = this.options,
+ pixelsFromWindowBottomToBottom = 0 + $(document).height() - (opts.binder.scrollTop()) - $(window).height();
+
+ // if behavior is defined and this function is extended, call that instead of default
+ if (!!opts.behavior && this['_nearbottom_'+opts.behavior] !== undefined) {
+ return this['_nearbottom_'+opts.behavior].call(this);
+ }
+
+ this._debug('math:', pixelsFromWindowBottomToBottom, opts.pixelsFromNavToBottom);
+
+ // if distance remaining in the scroll (including buffer) is less than the orignal nav to bottom....
+ return (pixelsFromWindowBottomToBottom - opts.bufferPx < opts.pixelsFromNavToBottom);
+
+ },
+
+ // Pause / temporarily disable plugin from firing
+ _pausing: function infscr_pausing(pause) {
+
+ var opts = this.options;
+
+ // if behavior is defined and this function is extended, call that instead of default
+ if (!!opts.behavior && this['_pausing_'+opts.behavior] !== undefined) {
+ this['_pausing_'+opts.behavior].call(this,pause);
+ return;
+ }
+
+ // If pause is not 'pause' or 'resume', toggle it's value
+ if (pause !== 'pause' && pause !== 'resume' && pause !== null) {
+ this._debug('Invalid argument. Toggling pause value instead');
+ }
+
+ pause = (pause && (pause === 'pause' || pause === 'resume')) ? pause : 'toggle';
+
+ switch (pause) {
+ case 'pause':
+ opts.state.isPaused = true;
+ break;
+
+ case 'resume':
+ opts.state.isPaused = false;
+ break;
+
+ case 'toggle':
+ opts.state.isPaused = !opts.state.isPaused;
+ break;
+ }
+
+ this._debug('Paused', opts.state.isPaused);
+ return false;
+
+ },
+
+ // Behavior is determined
+ // If the behavior option is undefined, it will set to default and bind to scroll
+ _setup: function infscr_setup() {
+
+ var opts = this.options;
+
+ // if behavior is defined and this function is extended, call that instead of default
+ if (!!opts.behavior && this['_setup_'+opts.behavior] !== undefined) {
+ this['_setup_'+opts.behavior].call(this);
+ return;
+ }
+
+ this._binding('bind');
+
+ return false;
+
+ },
+
+ // Show done message
+ _showdonemsg: function infscr_showdonemsg() {
+
+ var opts = this.options;
+
+ // if behavior is defined and this function is extended, call that instead of default
+ if (!!opts.behavior && this['_showdonemsg_'+opts.behavior] !== undefined) {
+ this['_showdonemsg_'+opts.behavior].call(this);
+ return;
+ }
+
+ opts.loading.msg
+ .find('img')
+ .hide()
+ .parent()
+ .find('div').html(opts.loading.finishedMsg).animate({ opacity: 1 }, 2000, function () {
+ $(this).parent().fadeOut(opts.loading.speed);
+ });
+
+ // user provided callback when done
+ opts.errorCallback.call($(opts.contentSelector)[0],'done');
+ },
+
+ // grab each selector option and see if any fail
+ _validate: function infscr_validate(opts) {
+ for (var key in opts) {
+ if (key.indexOf && key.indexOf('Selector') > -1 && $(opts[key]).length === 0) {
+ this._debug('Your ' + key + ' found no elements.');
+ return false;
+ }
+ }
+
+ return true;
+ },
+
+ /*
+ ----------------------------
+ Public methods
+ ----------------------------
+ */
+
+ // Bind to scroll
+ bind: function infscr_bind() {
+ this._binding('bind');
+ },
+
+ // Destroy current instance of plugin
+ destroy: function infscr_destroy() {
+ this.options.state.isDestroyed = true;
+ this.options.loading.finished();
+ return this._error('destroy');
+ },
+
+ // Set pause value to false
+ pause: function infscr_pause() {
+ this._pausing('pause');
+ },
+
+ // Set pause value to false
+ resume: function infscr_resume() {
+ this._pausing('resume');
+ },
+
+ beginAjax: function infscr_ajax(opts) {
+ var instance = this,
+ path = opts.path,
+ box, desturl, method, condition;
+
+ // increment the URL bit. e.g. /page/3/
+ opts.state.currPage++;
+
+ // Manually control maximum page
+ if ( opts.maxPage !== undefined && opts.state.currPage > opts.maxPage ){
+ opts.state.isBeyondMaxPage = true;
+ this.destroy();
+ return;
+ }
+
+ // if we're dealing with a table we can't use DIVs
+ box = $(opts.contentSelector).is('table, tbody') ? $('
') : $('
');
+
+ desturl = (typeof path === 'function') ? path(opts.state.currPage) : path.join(opts.state.currPage);
+ instance._debug('heading into ajax', desturl);
+
+ method = (opts.dataType === 'html' || opts.dataType === 'json' ) ? opts.dataType : 'html+callback';
+ if (opts.appendCallback && opts.dataType === 'html') {
+ method += '+callback';
+ }
+
+ switch (method) {
+ case 'html+callback':
+ instance._debug('Using HTML via .load() method');
+ box.load(desturl + ' ' + opts.itemSelector, undefined, function infscr_ajax_callback(responseText) {
+ instance._loadcallback(box, responseText, desturl);
+ });
+
+ break;
+
+ case 'html':
+ instance._debug('Using ' + (method.toUpperCase()) + ' via $.ajax() method');
+ $.ajax({
+ // params
+ url: desturl,
+ dataType: opts.dataType,
+ complete: function infscr_ajax_callback(jqXHR, textStatus) {
+ condition = (typeof (jqXHR.isResolved) !== 'undefined') ? (jqXHR.isResolved()) : (textStatus === "success" || textStatus === "notmodified");
+ if (condition) {
+ instance._loadcallback(box, jqXHR.responseText, desturl);
+ } else {
+ instance._error('end');
+ }
+ }
+ });
+
+ break;
+ case 'json':
+ instance._debug('Using ' + (method.toUpperCase()) + ' via $.ajax() method');
+ $.ajax({
+ dataType: 'json',
+ type: 'GET',
+ url: desturl,
+ success: function (data, textStatus, jqXHR) {
+ condition = (typeof (jqXHR.isResolved) !== 'undefined') ? (jqXHR.isResolved()) : (textStatus === "success" || textStatus === "notmodified");
+ if (opts.appendCallback) {
+ // if appendCallback is true, you must defined template in options.
+ // note that data passed into _loadcallback is already an html (after processed in opts.template(data)).
+ if (opts.template !== undefined) {
+ var theData = opts.template(data);
+ box.append(theData);
+ if (condition) {
+ instance._loadcallback(box, theData);
+ } else {
+ instance._error('end');
+ }
+ } else {
+ instance._debug("template must be defined.");
+ instance._error('end');
+ }
+ } else {
+ // if appendCallback is false, we will pass in the JSON object. you should handle it yourself in your callback.
+ if (condition) {
+ instance._loadcallback(box, data, desturl);
+ } else {
+ instance._error('end');
+ }
+ }
+ },
+ error: function() {
+ instance._debug("JSON ajax request failed.");
+ instance._error('end');
+ }
+ });
+
+ break;
+ }
+ },
+
+ // Retrieve next set of content items
+ retrieve: function infscr_retrieve(pageNum) {
+ pageNum = pageNum || null;
+
+ var instance = this,
+ opts = instance.options;
+
+ // if behavior is defined and this function is extended, call that instead of default
+ if (!!opts.behavior && this['retrieve_'+opts.behavior] !== undefined) {
+ this['retrieve_'+opts.behavior].call(this,pageNum);
+ return;
+ }
+
+ // for manual triggers, if destroyed, get out of here
+ if (opts.state.isDestroyed) {
+ this._debug('Instance is destroyed');
+ return false;
+ }
+
+ // we dont want to fire the ajax multiple times
+ opts.state.isDuringAjax = true;
+
+ opts.loading.start.call($(opts.contentSelector)[0],opts);
+ },
+
+ // Check to see next page is needed
+ scroll: function infscr_scroll() {
+
+ var opts = this.options,
+ state = opts.state;
+
+ // if behavior is defined and this function is extended, call that instead of default
+ if (!!opts.behavior && this['scroll_'+opts.behavior] !== undefined) {
+ this['scroll_'+opts.behavior].call(this);
+ return;
+ }
+
+ if (state.isDuringAjax || state.isInvalidPage || state.isDone || state.isDestroyed || state.isPaused) {
+ return;
+ }
+
+ if (!this._nearbottom()) {
+ return;
+ }
+
+ this.retrieve();
+
+ },
+
+ // Toggle pause value
+ toggle: function infscr_toggle() {
+ this._pausing();
+ },
+
+ // Unbind from scroll
+ unbind: function infscr_unbind() {
+ this._binding('unbind');
+ },
+
+ // update options
+ update: function infscr_options(key) {
+ if ($.isPlainObject(key)) {
+ this.options = $.extend(true,this.options,key);
+ }
+ }
+ };
+
+
+ /*
+ ----------------------------
+ Infinite Scroll function
+ ----------------------------
+
+ Borrowed logic from the following...
+
+ jQuery UI
+ - https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.widget.js
+
+ jCarousel
+ - https://github.com/jsor/jcarousel/blob/master/lib/jquery.jcarousel.js
+
+ Masonry
+ - https://github.com/desandro/masonry/blob/master/jquery.masonry.js
+
+*/
+
+ $.fn.infinitescroll = function infscr_init(options, callback) {
+
+
+ var thisCall = typeof options;
+
+ switch (thisCall) {
+
+ // method
+ case 'string':
+ var args = Array.prototype.slice.call(arguments, 1);
+
+ this.each(function () {
+ var instance = $.data(this, 'infinitescroll');
+
+ if (!instance) {
+ // not setup yet
+ // return $.error('Method ' + options + ' cannot be called until Infinite Scroll is setup');
+ return false;
+ }
+
+ if (!$.isFunction(instance[options]) || options.charAt(0) === "_") {
+ // return $.error('No such method ' + options + ' for Infinite Scroll');
+ return false;
+ }
+
+ // no errors!
+ instance[options].apply(instance, args);
+ });
+
+ break;
+
+ // creation
+ case 'object':
+
+ this.each(function () {
+
+ var instance = $.data(this, 'infinitescroll');
+
+ if (instance) {
+
+ // update options of current instance
+ instance.update(options);
+
+ } else {
+
+ // initialize new instance
+ instance = new $.infinitescroll(options, callback, this);
+
+ // don't attach if instantiation failed
+ if (!instance.failed) {
+ $.data(this, 'infinitescroll', instance);
+ }
+
+ }
+
+ });
+
+ break;
+
+ }
+
+ return this;
+ };
+
+
+
+ /*
+ * smartscroll: debounced scroll event for jQuery *
+ * https://github.com/lukeshumard/smartscroll
+ * Based on smartresize by @louis_remi: https://github.com/lrbabe/jquery.smartresize.js *
+ * Copyright 2011 Louis-Remi & Luke Shumard * Licensed under the MIT license. *
+ */
+
+ var event = $.event,
+ scrollTimeout;
+
+ event.special.smartscroll = {
+ setup: function () {
+ $(this).bind("scroll", event.special.smartscroll.handler);
+ },
+ teardown: function () {
+ $(this).unbind("scroll", event.special.smartscroll.handler);
+ },
+ handler: function (event, execAsap) {
+ // Save the context
+ var context = this,
+ args = arguments;
+
+ // set correct event type
+ event.type = "smartscroll";
+
+ if (scrollTimeout) { clearTimeout(scrollTimeout); }
+ scrollTimeout = setTimeout(function () {
+ $(context).trigger('smartscroll', args);
+ }, execAsap === "execAsap" ? 0 : 100);
+ }
+ };
+
+ $.fn.smartscroll = function (fn) {
+ return fn ? this.bind("smartscroll", fn) : this.trigger("smartscroll", ["execAsap"]);
+ };
+
+}));