lazy_loading_page 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +120 -0
  4. data/README.rdoc +3 -0
  5. data/Rakefile +37 -0
  6. data/app/assets/javascripts/lazy_load.js +196 -0
  7. data/app/assets/stylesheets/lazy_load.css +49 -0
  8. data/app/helpers/lazy_loading_page/application_helper.rb +18 -0
  9. data/config/routes.rb +2 -0
  10. data/lib/lazy_loading_page.rb +4 -0
  11. data/lib/lazy_loading_page/engine.rb +11 -0
  12. data/lib/lazy_loading_page/version.rb +3 -0
  13. data/lib/tasks/lazy_loading_page_tasks.rake +4 -0
  14. data/test/dummy/README.rdoc +28 -0
  15. data/test/dummy/Rakefile +6 -0
  16. data/test/dummy/app/assets/javascripts/application.js +13 -0
  17. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  18. data/test/dummy/app/controllers/application_controller.rb +5 -0
  19. data/test/dummy/app/helpers/application_helper.rb +2 -0
  20. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  21. data/test/dummy/bin/bundle +3 -0
  22. data/test/dummy/bin/rails +4 -0
  23. data/test/dummy/bin/rake +4 -0
  24. data/test/dummy/bin/setup +29 -0
  25. data/test/dummy/config.ru +4 -0
  26. data/test/dummy/config/application.rb +26 -0
  27. data/test/dummy/config/boot.rb +5 -0
  28. data/test/dummy/config/database.yml +25 -0
  29. data/test/dummy/config/environment.rb +5 -0
  30. data/test/dummy/config/environments/development.rb +41 -0
  31. data/test/dummy/config/environments/production.rb +79 -0
  32. data/test/dummy/config/environments/test.rb +42 -0
  33. data/test/dummy/config/initializers/assets.rb +11 -0
  34. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  35. data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
  36. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  37. data/test/dummy/config/initializers/inflections.rb +16 -0
  38. data/test/dummy/config/initializers/mime_types.rb +4 -0
  39. data/test/dummy/config/initializers/session_store.rb +3 -0
  40. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  41. data/test/dummy/config/locales/en.yml +23 -0
  42. data/test/dummy/config/routes.rb +4 -0
  43. data/test/dummy/config/secrets.yml +22 -0
  44. data/test/dummy/public/404.html +67 -0
  45. data/test/dummy/public/422.html +67 -0
  46. data/test/dummy/public/500.html +66 -0
  47. data/test/dummy/public/favicon.ico +0 -0
  48. data/test/integration/navigation_test.rb +8 -0
  49. data/test/lazy_loading_page_test.rb +7 -0
  50. data/test/test_helper.rb +21 -0
  51. metadata +163 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5af87dfee621fc240bc5285b2dfea1f4e8c1be00
4
+ data.tar.gz: 3989e27efdfb2a07413b10105897f200256b2dc2
5
+ SHA512:
6
+ metadata.gz: 1a5b9e2d099300039bbc1ba4825f7a0ac2603c6bea5b348b7830fde5763e8089579d870d3cb2d2563256197e104921f3a48cf94a319ac2f454ae0be6a74915e7
7
+ data.tar.gz: 84bf6a37a8f9b7219fc61ba764d501c55539d0809ac078e9cc9b2158cfbf1b9a2d690fe71254c6ae469ff4aabfdf964e1470397fe5598c86c50d8378b7e395d8
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2017 Jignesh Satam
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,120 @@
1
+ # lazy_loading_page
2
+ ***Load page lazily as an when required***
3
+ ***
4
+
5
+
6
+ lazy_loading_page is a gem that helps to **load page lazily** that is as an **when required**. The gem ***reduces page load time*** and ***reduces server ram*** by loading important content of the page in the first call and then by triggering calls to load the remaining page. The gem give **Reactjs** like **functionality** in your ruby-on-rails application.
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's **Gemfile**:
11
+
12
+ ```ruby
13
+ gem 'lazy_loading_page'
14
+ ```
15
+
16
+ And then execute:
17
+
18
+ $ bundle
19
+
20
+ Or install it yourself as:
21
+
22
+ $ gem install lazy_loading_page
23
+
24
+ Add the following line to your **application.js** file:
25
+
26
+ //= require lazy_load
27
+
28
+ Add the following line to your **application.css** file:
29
+
30
+ *= require lazy_load
31
+
32
+ ## Usage
33
+ Call the following method from view file (.html.erb / .html.haml) and pass a url as a parameter
34
+
35
+ ### .html.erb example
36
+
37
+ **Passing url as string**
38
+ ```erb
39
+ <%= lazy_load("http://lazy-loading-page-app.herokuapp.com/notifications") %>
40
+ ```
41
+ **Passing url string with parameters**
42
+ ```erb
43
+ <%= lazy_load("http://lazy-loading-page-app.herokuapp.com/notifications?page=1&count=5") %>
44
+ ```
45
+ **Passing url as helper method**
46
+ ```erb
47
+ <%= lazy_load(notifications_path) %>
48
+ ```
49
+ **Passing url as helper method with parameters**
50
+ ```erb
51
+ <%= lazy_load(notifications_path(page: 1, count: 5)) %>
52
+ ```
53
+ ### Configuring lazy loading page
54
+ Configuration can be achieved by passing parameters to lazy_load method
55
+ Following are some **configuration examples**:
56
+ ```erb
57
+ <%= lazy_load(notifications_path(page: 1, count: 5), id: "my_lazy_loader", type: "script", success: "afterSuccess()") %>
58
+
59
+ <%= lazy_load(notifications_path, class: "lazy_loaders", loader: "off", type: "json", failure: "afterFailure()", complete: "afterComplete(my_arg1, my_arg2)") %>
60
+
61
+ <%= lazy_load("http://lazy-loading-page-app.herokuapp.com/notifications?page=1&count=5", later: true, id: "later_lazy_loader", failure: "onSuccess(my_arg1)", complete: "onComplete()") %>
62
+ ```
63
+ ### Note
64
+ When using configuration option `later: true` **explicit trigger** the request call.
65
+ By calling the function `delayedLoading(elementToLoad);` in your javascript and passing the **lazy_loading element/elements** as argument.
66
+ **lazy_loading element/elements has class** `lazy_load`
67
+
68
+ ***Checkout the*** [***Demo***](http://lazy-loading-page-app.herokuapp.com/)
69
+
70
+ ***lazy-loading-page app implementation*** [@Github](https://github.com/JigneshSatam/lazy_loading_page_app)
71
+
72
+
73
+ ### .html.haml example:
74
+ ```haml
75
+ = lazy_load("http://lazy-loading-page-app.herokuapp.com/notifications")
76
+
77
+ = lazy_load("http://lazy-loading-page-app.herokuapp.com/notifications?page=1&count=5")
78
+
79
+ = lazy_load(notifications_path)
80
+
81
+ = lazy_load(notifications_path(page: 1, count:5))
82
+
83
+ = lazy_load(notifications_path(page: 1, count: 5), id: "my_lazy_loader", type: "script", success: "afterSuccess()")
84
+
85
+ = lazy_load(notifications_path, class: "lazy_loaders", loader: "off", type: "json", failure: "afterFailure()", complete: "afterComplete(my_arg1, my_arg2)")
86
+
87
+ = lazy_load("http://lazy-loading-page-app.herokuapp.com/notifications?page=1&count=5", later: true, id: "later_lazy_loader", failure: "onSuccess(my_arg1)", complete: "onComplete()")
88
+ ```
89
+
90
+ ## Configurations:
91
+
92
+ |Attributes|Defaults|Options|Description
93
+ |:----------:|:--------:|:-------:|-----------|
94
+ |type|null|script/json|**Type of request** to be made.|
95
+ |later|null|true|This will **stop the implicit loading** of the request, so that user can **explicit load the request** when required.|
96
+ |id|null|`userdefined`|Can **attach ID** to the object loaded in the DOM, it can be **helpful in explicit loading** of the request.|
97
+ |loader|on|off|This can be used to **turn on / off** the **loading effect**.|
98
+ |class|null|`userdefined`|Can **attach class** to the object loaded in the DOM, it can be **helpful in explicit loading** of the request.|
99
+
100
+ ## Callbacks:
101
+
102
+ |Callback|Example|Description
103
+ |:-----:|-----------|---------|
104
+ |success| functionAfterSuccess(arg1, arg2); |This function will be called when the request is successful.|
105
+ |failure| functionAfterFailure(arg1, arg2); |This function will be called when the request fails.|
106
+ |complete| functionAfterComplete(arg1, arg2); |This function will be called when the request is completed.|
107
+
108
+ ## Development
109
+
110
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
111
+
112
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
113
+
114
+ ## Contributing
115
+
116
+ 1. Fork it ( https://github.com/[my-github-username]/lazy_loading_page/fork )
117
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
118
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
119
+ 4. Push to the branch (`git push origin my-new-feature`)
120
+ 5. Create a new Pull Request
data/README.rdoc ADDED
@@ -0,0 +1,3 @@
1
+ = LazyLoadingPage
2
+
3
+ This project rocks and uses MIT-LICENSE.
data/Rakefile ADDED
@@ -0,0 +1,37 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'LazyLoadingPage'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
18
+ load 'rails/tasks/engine.rake'
19
+
20
+
21
+ load 'rails/tasks/statistics.rake'
22
+
23
+
24
+
25
+ Bundler::GemHelper.install_tasks
26
+
27
+ require 'rake/testtask'
28
+
29
+ Rake::TestTask.new(:test) do |t|
30
+ t.libs << 'lib'
31
+ t.libs << 'test'
32
+ t.pattern = 'test/**/*_test.rb'
33
+ t.verbose = false
34
+ end
35
+
36
+
37
+ task default: :test
@@ -0,0 +1,196 @@
1
+ ("turbolinks:load DOMContentLoaded ready".split(" ")).forEach(function(e){
2
+ if (window.lazyLoaderInitialize === undefined) {
3
+ document.addEventListener(e, function() {
4
+ lazyLoad();
5
+ });
6
+ window.lazyLoaderInitialize = true;
7
+ }
8
+ });
9
+
10
+ function lazyLoad(){
11
+ var $lazy_loaders = document.querySelectorAll(".lazy_load:not([data-later='true']):not([data-loading='started'])");
12
+ lazyLoadElements($lazy_loaders);
13
+ }
14
+
15
+ function lazyLoadElements(elements){
16
+ for (var i = 0; i < elements.length; i++) {
17
+ (function(i){
18
+ $lazy_loader = elements[i];
19
+ lazyLoadElement($lazy_loader);
20
+ })(i);
21
+ };
22
+ }
23
+
24
+ function delayedLoading(ele){
25
+ if ((ele instanceof NodeList) || ((window.jQuery !== undefined || window.$ !== undefined) && (ele instanceof jQuery))) {
26
+ lazyLoadElements(ele);
27
+ }
28
+ else if(ele instanceof HTMLElement){
29
+ lazyLoadElement(ele);
30
+ }
31
+ }
32
+
33
+ function lazyLoadElement(ele){
34
+ if (!ele.classList.contains("lazy_load"))
35
+ return false;
36
+ var id = ele.getAttribute("data-id");
37
+ var url = ele.getAttribute("data-url");
38
+ if (ele.getAttribute("data-loader") != "false")
39
+ addLoader(ele);
40
+ var xhttp = new XMLHttpRequest();
41
+ xhttp.onreadystatechange = function(){ajaxCallback(xhttp, id);};
42
+ xhttp.open("GET", url, true);
43
+ if (ele.getAttribute("data-type") == "script")
44
+ xhttp.setRequestHeader('Accept', 'text/javascript, application/javascript, application/ecmascript, application/x-ecmascript, */*');
45
+ if (ele.getAttribute("data-type") == "json")
46
+ xhttp.setRequestHeader('Accept', "application/json, text/javascript, */*");
47
+ xhttp.setRequestHeader("X-CSRF-Token", document.querySelector("[name='csrf-token']").content);
48
+ xhttp.setRequestHeader("X-Requested-With", "XMLHttpRequest");
49
+ xhttp.send();
50
+ ele.setAttribute("data-loading", "started");
51
+ }
52
+
53
+ function addLoader(ele){
54
+ if (document.querySelector("[data-id='"+ ele.getAttribute("data-id") +"'][class='loading-container']"))
55
+ return false;
56
+ var loader_img = document.createElement("div");
57
+ loader_img.setAttribute("class", "loading-container");
58
+ loader_img.setAttribute("data-id", ele.getAttribute("data-id"));
59
+ if (["script", "json"].indexOf(ele.getAttribute("data-type")) >= 0)
60
+ loader_img.setAttribute("data-type", ele.getAttribute("data-type"));
61
+ if (ele.getAttribute("data-success") != null)
62
+ loader_img.setAttribute("data-success", ele.getAttribute("data-success"));
63
+ if (ele.getAttribute("data-failure") != null)
64
+ loader_img.setAttribute("data-failure", ele.getAttribute("data-failure"));
65
+ if (ele.getAttribute("data-complete") != null)
66
+ loader_img.setAttribute("data-complete", ele.getAttribute("data-complete"));
67
+ var span = document.createElement("span");
68
+ span.setAttribute("class", "loading-indicator");
69
+ for(var j=0; j<3; j++){
70
+ var i = document.createElement("i");
71
+ span.appendChild(i);
72
+ }
73
+ loader_img.appendChild(span);
74
+ ele.parentElement.replaceChild(loader_img, ele);
75
+ loader_img.appendChild(ele);
76
+ }
77
+
78
+ function ajaxCallback(xhttp, id) {
79
+ var elementToReplace = document.querySelectorAll("[data-id='"+id+"']")[0];
80
+ switch(xhttp.readyState) {
81
+ case XMLHttpRequest.UNSENT:
82
+ // code block
83
+ break;
84
+ case XMLHttpRequest.OPENED:
85
+ // code block
86
+ break;
87
+ case XMLHttpRequest.HEADERS_RECEIVED:
88
+ // code block
89
+ break;
90
+ case XMLHttpRequest.LOADING:
91
+ // code block
92
+ break;
93
+ case XMLHttpRequest.DONE:
94
+ requestComplete(xhttp, elementToReplace);
95
+ callbackFor(elementToReplace, "complete", xhttp);
96
+ break;
97
+ default:
98
+ requestComplete(xhttp, elementToReplace);
99
+ callbackFor(elementToReplace, "complete", xhttp);
100
+ break;
101
+ }
102
+ }
103
+
104
+ function requestComplete(xhttp, elementToReplace){
105
+ if (xhttp.status == 200) {
106
+ requestSuccess(xhttp, elementToReplace);
107
+ callbackFor(elementToReplace, "success", xhttp);
108
+ }
109
+ else{
110
+ requestFailure(xhttp, elementToReplace)
111
+ callbackFor(elementToReplace, "failure", xhttp);
112
+ }
113
+ }
114
+
115
+ function requestSuccess(xhttp, elementToReplace){
116
+ var parentElement = elementToReplace.parentNode;
117
+ if (elementToReplace.getAttribute("data-type") == "script"){
118
+ // if(true){
119
+ javascriptResponseActions(xhttp, parentElement, elementToReplace);
120
+ }
121
+ else if(elementToReplace.getAttribute("data-type") == "json"){
122
+ // JSON.parse(xhttp.response);
123
+ parentElement.removeChild(elementToReplace);
124
+ }
125
+ else{
126
+ plainResponseActions(xhttp, parentElement, elementToReplace);
127
+ }
128
+ lazyLoad();
129
+ }
130
+
131
+ function requestFailure(xhttp, elementToReplace){
132
+ if (elementToReplace.classList.contains("loading-container"))
133
+ elementToReplace = elementToReplace.querySelector('.lazy_load')
134
+ loadElement(elementToReplace);
135
+ }
136
+
137
+ function javascriptResponseActions(xhttp, parentElement, elementToReplace){
138
+ var newScript = document.createElement("script");
139
+ newScript.innerHTML = xhttp.responseText;
140
+ parentElement.insertBefore(newScript, elementToReplace);
141
+ parentElement.removeChild(elementToReplace);
142
+ }
143
+
144
+ function plainResponseActions(xhttp, parentElement, elementToReplace){
145
+ var parser = new DOMParser();
146
+ var doc = parser.parseFromString(xhttp.responseText, "text/html");
147
+ var newElements = doc.querySelector("body");
148
+ if (window.$ !== undefined){
149
+ // if (false){
150
+ new_ele = $(xhttp.responseText);
151
+ if (newElements !== null){
152
+ new_ele = $(newElements.innerHTML);
153
+ }
154
+ $(elementToReplace).replaceWith(new_ele);
155
+ }
156
+ else{
157
+ var template = document.createElement("template");
158
+ if (newElements !== null){
159
+ template.innerHTML = newElements.innerHTML;
160
+ }
161
+ else{
162
+ template.innerHTML = xhttp.responseText;
163
+ }
164
+ var childNodes = template.content.childNodes
165
+ var newElement = elementToReplace
166
+ var scriptTags = [];
167
+ for(var i= childNodes.length-1; i>=0 ; i--){
168
+ newPreviousElement = childNodes[i];
169
+ parentElement.insertBefore(newPreviousElement, newElement);
170
+ if(newPreviousElement.nodeName == "SCRIPT"){
171
+ scriptTags.push(newPreviousElement.textContent);
172
+ eval();
173
+ }
174
+ newElement = newPreviousElement;
175
+ }
176
+ runScripts(scriptTags);
177
+ parentElement.removeChild(elementToReplace);
178
+ }
179
+ }
180
+
181
+ function runScripts(scriptTags){
182
+ scriptTags.forEach( function(scriptTag){
183
+ eval(scriptTag)
184
+ });
185
+ }
186
+
187
+ function callbackFor(ele, option, xhttp){
188
+ var userFunction = ele.getAttribute("data-"+ option);
189
+ if (userFunction !== null){
190
+ userFunctionArray = userFunction.split("(");
191
+ var extractAttributes = userFunctionArray[1].split(")")[0];
192
+ var attributes = extractAttributes.split(",").map(function(arg){return arg.trim()})
193
+ var functionName = userFunctionArray[0];
194
+ eval(functionName).apply(xhttp, attributes);
195
+ }
196
+ }
@@ -0,0 +1,49 @@
1
+ @-webkit-keyframes bouncedelay {
2
+ 0%, 100%, 80% {
3
+ -webkit-transform: scale(0);
4
+ transform: scale(0)
5
+ }
6
+ 40% {
7
+ -webkit-transform: scale(1);
8
+ transform: scale(1)
9
+ }
10
+ }
11
+ @keyframes bouncedelay {
12
+ 0%, 100%, 80% {
13
+ -webkit-transform: scale(0);
14
+ transform: scale(0)
15
+ }
16
+ 40% {
17
+ -webkit-transform: scale(1);
18
+ transform: scale(1)
19
+ }
20
+ }
21
+ .loading-indicator{
22
+ display: inline-block;
23
+ vertical-align: middle;
24
+ }
25
+ .loading-indicator i{
26
+ position: relative;
27
+ display: inline-block;
28
+ width: 8px;
29
+ height: 8px;
30
+ background-color: #A6ADAD;
31
+ border-radius: 50%;
32
+ line-height: 0;
33
+ -webkit-transform-origin: center center;
34
+ transform-origin: center center;
35
+ -webkit-animation: bouncedelay 1.3s infinite linear;
36
+ animation: bouncedelay 1.3s infinite linear
37
+ }
38
+ .loading-indicator i:nth-child(2){
39
+ -webkit-animation-delay: .3s;
40
+ animation-delay: .3s
41
+ }
42
+ .loading-indicator i:nth-child(3){
43
+ -webkit-animation-delay: .6s;
44
+ animation-delay: .6s
45
+ }
46
+ .loading-container {
47
+ text-align: center;
48
+ padding: 1em 1em
49
+ }
@@ -0,0 +1,18 @@
1
+ module LazyLoadingPage
2
+ module ApplicationHelper
3
+ def lazy_load(url, options = {})
4
+ options = options.symbolize_keys
5
+ id = options[:id].presence
6
+ classes = ("lazy_load " + options[:class] rescue "lazy_load")
7
+ params = options[:params]
8
+ data_merge_hash = {}
9
+ options[:later].present? && (options[:later].to_s.downcase == "true") && (data_merge_hash[:later] = true)
10
+ options[:loader].present? && (options[:loader].to_s.downcase == "off") && (data_merge_hash[:loader] = false)
11
+ options[:type].present? && (["script", "json"].include?(options[:type])) && (data_merge_hash[:type] = options[:type])
12
+ options[:success].present? && data_merge_hash[:success] = options[:success]
13
+ options[:failure].present? && data_merge_hash[:failure] = options[:failure]
14
+ options[:complete].present? && data_merge_hash[:complete] = options[:complete]
15
+ content_tag("input", nil, type:"hidden", id: id, class: classes, data: {id: SecureRandom.uuid, url: url}.merge!(data_merge_hash))
16
+ end
17
+ end
18
+ end