bookends 2.8.0 → 3.0.0.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +0 -8
  3. data/{fixtures/dummy-rails/app → app}/assets/fonts/bentonsans-book.eot +0 -0
  4. data/{fixtures/dummy-rails/app → app}/assets/fonts/bentonsans-book.svg +0 -0
  5. data/{fixtures/dummy-rails/app → app}/assets/fonts/bentonsans-book.ttf +0 -0
  6. data/{fixtures/dummy-rails/app → app}/assets/fonts/bentonsans-book.woff +0 -0
  7. data/{fixtures/dummy-rails/app → app}/assets/fonts/bentonsans-medium.eot +0 -0
  8. data/{fixtures/dummy-rails/app → app}/assets/fonts/bentonsans-medium.svg +0 -0
  9. data/{fixtures/dummy-rails/app → app}/assets/fonts/bentonsans-medium.ttf +0 -0
  10. data/{fixtures/dummy-rails/app → app}/assets/fonts/bentonsans-medium.woff +0 -0
  11. data/{fixtures/dummy-rails/app → app}/assets/fonts/bentonsans-regular.eot +0 -0
  12. data/{fixtures/dummy-rails/app → app}/assets/fonts/bentonsans-regular.svg +0 -0
  13. data/{fixtures/dummy-rails/app → app}/assets/fonts/bentonsans-regular.ttf +0 -0
  14. data/{fixtures/dummy-rails/app → app}/assets/fonts/bentonsans-regular.woff +0 -0
  15. data/app/assets/fonts/glyphicons-halflings-regular.eot +0 -0
  16. data/app/assets/fonts/glyphicons-halflings-regular.svg +288 -0
  17. data/app/assets/fonts/glyphicons-halflings-regular.ttf +0 -0
  18. data/app/assets/fonts/glyphicons-halflings-regular.woff +0 -0
  19. data/app/assets/fonts/glyphicons-halflings-regular.woff2 +0 -0
  20. data/app/assets/fonts/hk-icon.eot +0 -0
  21. data/app/assets/fonts/hk-icon.svg +268 -0
  22. data/app/assets/fonts/hk-icon.ttf +0 -0
  23. data/app/assets/fonts/hk-icon.woff +0 -0
  24. data/app/assets/fonts/inconsolata-bold.eot +0 -0
  25. data/app/assets/fonts/inconsolata-bold.svg +239 -0
  26. data/app/assets/fonts/inconsolata-bold.ttf +0 -0
  27. data/app/assets/fonts/inconsolata-bold.woff +0 -0
  28. data/app/assets/fonts/inconsolata-regular.eot +0 -0
  29. data/app/assets/fonts/inconsolata-regular.svg +239 -0
  30. data/app/assets/fonts/inconsolata-regular.ttf +0 -0
  31. data/app/assets/fonts/inconsolata-regular.woff +0 -0
  32. data/{lib/bookends/templates/footer_images → app/assets/images/bookends/footer}/footer_sprite.png +0 -0
  33. data/app/assets/images/bookends/footer/footer_sprite@2x.png +0 -0
  34. data/{lib/bookends/templates/footer_images → app/assets/images/bookends/footer}/salesforce.png +0 -0
  35. data/{lib/bookends/templates → app/assets/images/bookends}/glostick_icons/dashboard.svg +0 -0
  36. data/{lib/bookends/templates → app/assets/images/bookends}/glostick_icons/databases.svg +0 -0
  37. data/{lib/bookends/templates → app/assets/images/bookends}/glostick_icons/dataclips.svg +0 -0
  38. data/{lib/bookends/templates → app/assets/images/bookends}/glostick_icons/docs.svg +0 -0
  39. data/{lib/bookends/templates → app/assets/images/bookends}/glostick_icons/elements.svg +0 -0
  40. data/{lib/bookends/templates → app/assets/images/bookends}/glostick_icons/heroku-logo.svg +0 -0
  41. data/{lib/bookends/templates → app/assets/images/bookends}/glostick_icons/navigator-purple.svg +0 -0
  42. data/{lib/bookends/templates → app/assets/images/bookends}/glostick_icons/navigator.svg +0 -0
  43. data/{lib/bookends/templates → app/assets/images/bookends}/glostick_icons/settings.svg +0 -0
  44. data/{lib/bookends/templates → app/assets/images/bookends}/glostick_icons/signout.svg +0 -0
  45. data/{lib/bookends/templates → app/assets/images/bookends}/glostick_icons/support.svg +0 -0
  46. data/{lib/bookends/templates → app/assets/images/bookends}/icons/addons.svg +0 -0
  47. data/{lib/bookends/templates → app/assets/images/bookends}/icons/buildpacks.svg +0 -0
  48. data/{lib/bookends/templates → app/assets/images/bookends}/icons/buttons.svg +0 -0
  49. data/{lib/bookends/templates → app/assets/images/bookends}/icons/connect.svg +0 -0
  50. data/{lib/bookends/templates → app/assets/images/bookends}/icons/elements-information.svg +0 -0
  51. data/{lib/bookends/templates → app/assets/images/bookends}/icons/enterprise.svg +0 -0
  52. data/{lib/bookends/templates → app/assets/images/bookends}/icons/platform.svg +0 -0
  53. data/{lib/bookends/templates → app/assets/images/bookends}/icons/postgres.svg +0 -0
  54. data/{lib/bookends/templates → app/assets/images/bookends}/icons/redis.svg +0 -0
  55. data/{lib/bookends/templates → app/assets/images/bookends}/icons/teams.svg +0 -0
  56. data/app/assets/images/logo.svg +14 -0
  57. data/app/assets/images/nav-dropdown-caret.svg +16 -0
  58. data/app/assets/javascripts/blog-fetcher.js +131 -0
  59. data/{fixtures/dummy-rails/app/assets/javascripts/application.js → app/assets/javascripts/bookends.js} +1 -2
  60. data/{lib/bookends/templates/js → app/assets/javascripts}/header.js +0 -0
  61. data/app/assets/javascripts/heroku-namespace.js +1 -0
  62. data/app/assets/javascripts/lscache.js +359 -0
  63. data/{lib/bookends/templates/js → app/assets/javascripts}/md5.js +0 -0
  64. data/app/assets/javascripts/moment.js +3195 -0
  65. data/{lib/bookends/templates/js → app/assets/javascripts}/newsletter-init.js +1 -1
  66. data/{lib/bookends/templates/js → app/assets/javascripts}/newsletter.js +0 -0
  67. data/app/assets/stylesheets/bookends/_base.scss +21 -0
  68. data/app/assets/stylesheets/bookends/_colors.scss +62 -0
  69. data/app/assets/stylesheets/bookends/_fonts.scss +12 -0
  70. data/{lib/bookends/templates/scss/footer.scss → app/assets/stylesheets/bookends/_footer.scss} +17 -14
  71. data/{lib/bookends/templates/scss/glostick.scss → app/assets/stylesheets/bookends/_glostick.scss} +0 -0
  72. data/app/assets/stylesheets/bookends/_grid-settings.scss +18 -0
  73. data/app/assets/stylesheets/bookends/_header.scss +745 -0
  74. data/app/assets/stylesheets/bookends/_mixins.scss +72 -0
  75. data/app/assets/stylesheets/bookends/_responsive.scss +129 -0
  76. data/app/assets/stylesheets/bookends/_variables.scss +82 -0
  77. data/app/assets/stylesheets/bookends/bookends.scss +13 -0
  78. data/app/controllers/bookends/application_controller.rb +5 -0
  79. data/app/helpers/bookends/application_helper.rb +93 -0
  80. data/{lib/bookends/templates/footer.html → app/views/bookends/_footer.html.erb} +4 -6
  81. data/{lib/bookends/templates/rails_header.html.erb → app/views/bookends/_header.html.erb} +1 -2
  82. data/config/routes.rb +2 -0
  83. data/lib/bookends.rb +2 -2
  84. data/lib/bookends/config.rb +24 -0
  85. data/lib/bookends/engine.rb +15 -2
  86. data/lib/bookends/version.rb +1 -1
  87. data/lib/tasks/bookends_tasks.rake +4 -0
  88. metadata +221 -152
  89. data/.gitignore +0 -17
  90. data/.rspec +0 -2
  91. data/.ruby-version +0 -1
  92. data/.travis.yml +0 -8
  93. data/Gemfile +0 -4
  94. data/bin/bookends +0 -5
  95. data/bin/setup +0 -7
  96. data/bookends.gemspec +0 -31
  97. data/fixtures/dummy-jekyll/.gitkeep +0 -0
  98. data/fixtures/dummy-rails/README.md +0 -9
  99. data/fixtures/dummy-rails/Rakefile +0 -6
  100. data/fixtures/dummy-rails/app/assets/images/.keep +0 -0
  101. data/fixtures/dummy-rails/app/assets/images/bookends/footer/footer_sprite.png +0 -0
  102. data/fixtures/dummy-rails/app/assets/images/bookends/footer/footer_sprite@2x.png +0 -0
  103. data/fixtures/dummy-rails/app/assets/images/bookends/footer/salesforce_heroku_gray.png +0 -0
  104. data/fixtures/dummy-rails/app/assets/stylesheets/application.css.scss +0 -11
  105. data/fixtures/dummy-rails/app/controllers/application_controller.rb +0 -5
  106. data/fixtures/dummy-rails/app/controllers/concerns/.keep +0 -0
  107. data/fixtures/dummy-rails/app/controllers/pages_controller.rb +0 -5
  108. data/fixtures/dummy-rails/app/helpers/application_helper.rb +0 -2
  109. data/fixtures/dummy-rails/app/mailers/.keep +0 -0
  110. data/fixtures/dummy-rails/app/models/.keep +0 -0
  111. data/fixtures/dummy-rails/app/models/concerns/.keep +0 -0
  112. data/fixtures/dummy-rails/app/views/layouts/application.html.erb +0 -16
  113. data/fixtures/dummy-rails/app/views/pages/home.html.erb +0 -1
  114. data/fixtures/dummy-rails/bin/bundle +0 -3
  115. data/fixtures/dummy-rails/bin/rails +0 -4
  116. data/fixtures/dummy-rails/bin/rake +0 -4
  117. data/fixtures/dummy-rails/bin/setup +0 -29
  118. data/fixtures/dummy-rails/config.ru +0 -4
  119. data/fixtures/dummy-rails/config/application.rb +0 -34
  120. data/fixtures/dummy-rails/config/boot.rb +0 -5
  121. data/fixtures/dummy-rails/config/database.yml +0 -25
  122. data/fixtures/dummy-rails/config/environment.rb +0 -5
  123. data/fixtures/dummy-rails/config/environments/development.rb +0 -41
  124. data/fixtures/dummy-rails/config/environments/production.rb +0 -79
  125. data/fixtures/dummy-rails/config/environments/test.rb +0 -42
  126. data/fixtures/dummy-rails/config/initializers/assets.rb +0 -11
  127. data/fixtures/dummy-rails/config/initializers/backtrace_silencers.rb +0 -7
  128. data/fixtures/dummy-rails/config/initializers/cookies_serializer.rb +0 -3
  129. data/fixtures/dummy-rails/config/initializers/filter_parameter_logging.rb +0 -4
  130. data/fixtures/dummy-rails/config/initializers/inflections.rb +0 -16
  131. data/fixtures/dummy-rails/config/initializers/mime_types.rb +0 -4
  132. data/fixtures/dummy-rails/config/initializers/session_store.rb +0 -3
  133. data/fixtures/dummy-rails/config/initializers/wrap_parameters.rb +0 -14
  134. data/fixtures/dummy-rails/config/locales/en.yml +0 -23
  135. data/fixtures/dummy-rails/config/routes.rb +0 -6
  136. data/fixtures/dummy-rails/config/secrets.yml +0 -22
  137. data/fixtures/dummy-rails/db/development.sqlite3 +0 -0
  138. data/fixtures/dummy-rails/lib/assets/.keep +0 -0
  139. data/fixtures/dummy-rails/public/404.html +0 -67
  140. data/fixtures/dummy-rails/public/422.html +0 -67
  141. data/fixtures/dummy-rails/public/500.html +0 -66
  142. data/fixtures/dummy-rails/public/favicon.ico +0 -0
  143. data/lib/bookends/cli.rb +0 -95
  144. data/lib/bookends/config/framework.rb +0 -24
  145. data/lib/bookends/config/turbolinks.rb +0 -18
  146. data/lib/bookends/file_actions.rb +0 -13
  147. data/lib/bookends/flag_handler.rb +0 -17
  148. data/lib/bookends/jekyll_handler.rb +0 -25
  149. data/lib/bookends/rails_handler.rb +0 -41
  150. data/lib/bookends/templates/bookends_helper.rb +0 -63
  151. data/lib/bookends/templates/bookends_helper_spec.rb +0 -57
  152. data/lib/bookends/templates/footer_images/footer_sprite@2x.png +0 -0
  153. data/lib/bookends/templates/icons/kafka.svg +0 -37
  154. data/lib/bookends/templates/icons/spaces.svg +0 -1
  155. data/lib/bookends/templates/jekyll_header.html +0 -102
  156. data/lib/bookends/templates/js/header-turbolinks.js +0 -18
  157. data/lib/bookends/templates/js/newsletter-init-turbolinks.js +0 -6
  158. data/lib/bookends/templates/scss/bookends.scss +0 -21
@@ -0,0 +1,14 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <svg width="143px" height="40px" viewBox="0 0 143 40" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
3
+ <!-- Generator: Sketch 3.3.3 (12072) - http://www.bohemiancoding.com/sketch -->
4
+ <title>logo</title>
5
+ <desc>Created with Sketch.</desc>
6
+ <defs></defs>
7
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
8
+ <g id="main" sketch:type="MSArtboardGroup" transform="translate(-25.000000, -23.000000)" fill="#FFFFFF">
9
+ <g id="nav" sketch:type="MSLayerGroup" transform="translate(25.000000, 23.000000)">
10
+ <path d="M32.274167,0 L3.58601855,0 C1.60573942,0 0,1.612 0,3.6 L0,36.4 C0,38.388 1.60573942,40 3.58601855,40 L32.274167,40 C34.2544461,40 35.8601855,38.388 35.8601855,36.4 L35.8601855,3.6 C35.8601855,1.612 34.2544461,0 32.274167,0 Z M33.867953,36.4 C33.867953,37.2825 33.1532396,38 32.274167,38 L3.58601855,38 C2.70744401,38 1.99223253,37.2825 1.99223253,36.4 L1.99223253,3.6 C1.99223253,2.7175 2.70744401,2 3.58601855,2 L32.274167,2 C33.1532396,2 33.867953,2.7175 33.867953,3.6 L33.867953,36.4 Z M8.96504638,34 L13.4475696,30 L8.96504638,26 L8.96504638,34 Z M25.1604027,17.7875 C24.357533,16.972 22.8832809,16 20.4203834,16 C17.7194142,16 14.9387556,16.7055 12.9495114,17.3555 L12.9495114,6 L8.96504638,6 L8.96504638,23.346 L11.7795729,22.066 C11.8248962,22.0455 16.3691786,20 20.4203834,20 C22.4429975,20 22.8912498,21.1215 22.9106741,22.055 L22.9106741,34 L26.8951392,34 L26.8951392,22 C26.8976294,21.744 26.8737227,19.5275 25.1604027,17.7875 Z M19.9223253,12.5 L23.9067904,12.5 C25.7087647,10.45 26.6261878,8.272 26.8951392,6 L22.9106741,6 C22.4713868,8.267 21.4882201,10.4375 19.9223253,12.5 Z M130.435895,21.9021404 L130.435895,13.0509941 L134.436196,13.0509941 L134.436196,21.8825993 C134.436196,23.9193901 135.096308,24.838326 136.69693,24.838326 C138.296549,24.838326 138.936597,23.9193901 138.936597,21.8825993 L138.936597,13.0509941 L142.857143,13.0509941 L142.857143,21.9021404 C142.857143,25.8188917 140.977127,28.0556037 136.656802,28.0556037 C132.335975,28.0556037 130.435895,25.8188917 130.435895,21.9021404 Z M113.581962,13.0509941 L117.581762,13.0509941 L117.581762,18.4062758 L121.662821,13.0509941 L126.30317,13.0509941 L121.322231,18.8852849 L126.742576,27.8361414 L122.222111,27.8361414 L118.722412,21.9216816 L117.581762,23.2404598 L117.581762,27.8361414 L113.581962,27.8361414 L113.581962,13.0509941 Z M94.5876806,20.4430667 C94.5876806,15.0296624 98.2875201,12.770904 101.867978,12.770904 C105.428371,12.770904 109.128712,15.0296624 109.128712,20.4430667 C109.128712,25.857473 105.428371,28.1152293 101.867978,28.1152293 C98.2875201,28.1152293 94.5876806,25.857473 94.5876806,20.4430667 Z M105.127909,20.4430667 C105.127909,17.7258424 103.948134,16.0678496 101.867978,16.0678496 C99.7677568,16.0678496 98.5879815,17.7258424 98.5879815,20.4430667 C98.5879815,23.1612931 99.7677568,24.9189959 101.867978,24.9189959 C103.948134,24.9189959 105.127909,23.1612931 105.127909,20.4430667 Z M78.5132424,13.0509941 L84.29374,13.0509941 C88.0939005,13.0509941 90.4143258,14.3101467 90.4143258,17.8661379 C90.4143258,20.1835199 89.4336878,21.5228414 87.4939807,22.1812284 L90.6340289,27.8361414 L86.3939607,27.8361414 L83.5333066,22.7018251 L82.4332865,22.7018251 L82.4332865,27.8361414 L78.5132424,27.8361414 L78.5132424,13.0509941 Z M84.1532905,19.6639253 C85.7935393,19.6639253 86.5133427,19.2044573 86.5133427,17.965848 C86.5133427,16.7277397 85.7935393,16.2873119 84.1532905,16.2873119 L82.4332865,16.2873119 L82.4332865,19.6639253 L84.1532905,19.6639253 Z M63.2182986,13.0509941 L73.7790931,13.0509941 L73.7790931,16.3679818 L67.2185995,16.3679818 L67.2185995,18.7655327 L71.899077,18.7655327 L71.899077,21.8825993 L67.2185995,21.8825993 L67.2185995,24.6193647 L74.119683,24.6193647 L74.119683,27.8361414 L63.2182986,27.8361414 L63.2182986,13.0509941 Z M45.1444623,13.0509941 L49.1447632,13.0509941 L49.1447632,18.5450682 L54.065008,18.5450682 L54.065008,13.0509941 L58.0658106,13.0509941 L58.0658106,27.8361414 L54.065008,27.8361414 L54.065008,21.862056 L49.1447632,21.862056 L49.1447632,27.8361414 L45.1444623,27.8361414 L45.1444623,13.0509941 Z" id="logo" sketch:type="MSShapeGroup"></path>
11
+ </g>
12
+ </g>
13
+ </g>
14
+ </svg>
@@ -0,0 +1,16 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <svg width="9px" height="6px" viewBox="0 0 9 6" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
3
+ <!-- Generator: Sketch 3.3.3 (12072) - http://www.bohemiancoding.com/sketch -->
4
+ <title>nav-dropdown-caret</title>
5
+ <desc>Created with Sketch.</desc>
6
+ <defs></defs>
7
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
8
+ <g id="main" sketch:type="MSArtboardGroup" transform="translate(-267.000000, -42.000000)" stroke="#F2EFFF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
9
+ <g id="nav" sketch:type="MSLayerGroup" transform="translate(25.000000, 23.000000)">
10
+ <g id="main" transform="translate(174.000000, 15.000000)" sketch:type="MSShapeGroup">
11
+ <path d="M75.6666667,5 L72.3333333,9 L69,5" id="nav-dropdown-caret"></path>
12
+ </g>
13
+ </g>
14
+ </g>
15
+ </g>
16
+ </svg>
@@ -0,0 +1,131 @@
1
+ //= require lscache
2
+ //= require moment
3
+
4
+ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
5
+
6
+ Heroku.BlogFetcher = (function() {
7
+ function BlogFetcher(container) {
8
+ this.handleError = bind(this.handleError, this);
9
+ this.appendData = bind(this.appendData, this);
10
+ this.$container = $(container);
11
+ this.init();
12
+ }
13
+
14
+ BlogFetcher.prototype.init = function() {
15
+ this.$container
16
+ .on('dataLoaded', this.appendData)
17
+ .on('dataError', this.handleError);
18
+ return this.getPosts();
19
+ };
20
+
21
+ BlogFetcher.prototype.getPosts = function() {
22
+ $.support.cors = true;
23
+ if (lscache.get('blogEntry')) {
24
+ return this.$container.trigger('dataLoaded');
25
+ } else {
26
+ var self = this;
27
+ return $.ajax({
28
+ url: 'https://blog.heroku.com/rss_feedburner',
29
+ type: 'GET',
30
+ dataType: 'xml',
31
+ crossDomain: true
32
+ })
33
+ .done(function(result) {
34
+ var obj = self.xmlToJson($(result).find('item')[0]);
35
+ var textBlock = obj.description['#text'];
36
+ var content = typeof textBlock === 'object' ? textBlock[0] : textBlock; // FF treats the textBlock as an object for some reason
37
+ var blog = {
38
+ 'title': obj.title['#text'],
39
+ 'contentSnippet': self.trimToLength(content, 200),
40
+ 'link': obj.link['#text'],
41
+ 'publishedDate': obj.pubDate['#text'],
42
+ 'author': obj.author['#text']
43
+ };
44
+ lscache.set('blogEntry', blog, 60);
45
+ return self.$container.trigger('dataLoaded');
46
+ })
47
+ .fail(function(e) {
48
+ return self.$container.trigger('dataError');
49
+ });
50
+ }
51
+ };
52
+
53
+ BlogFetcher.prototype.appendData = function() {
54
+ var blogContent = this.$container.find('.js-blog-content');
55
+ this.storedEntry = lscache.get('blogEntry');
56
+ this.$container.find('.js-blog-link').text(this.storedEntry.title);
57
+ this.$container.find('.js-blog-link').attr('href', this.storedEntry.link);
58
+ this.$container.find('.js-blog-date').html(this.buildDateAndAuthor());
59
+ blogContent.html(this.storedEntry.contentSnippet);
60
+ blogContent.find('p').append(' <a href="' + this.storedEntry.link + '">Read More</a>');
61
+ };
62
+
63
+ BlogFetcher.prototype.handleError = function() {
64
+ if (this.storedEntry) {
65
+ return this.$container.trigger('dataloaded');
66
+ } else {
67
+ this.$container.find('.js-blog-link').text('Visit the Heroku Blog');
68
+ this.$container.find('.js-blog-date').empty();
69
+ this.$container.find('.js-blog-content').text('Find news and updates from Heroku in the blog.');
70
+ }
71
+ };
72
+
73
+ BlogFetcher.prototype.buildDateAndAuthor = function() {
74
+ var ago = moment.utc(this.storedEntry.publishedDate, 'ddd, D MMM YYYY hh:mm:ss ZZ').fromNow();
75
+ return ago + " by " + this.storedEntry.author;
76
+ };
77
+
78
+ //http://davidwalsh.name/convert-xml-json
79
+ BlogFetcher.prototype.xmlToJson = function(xml) {
80
+ // Create the return object
81
+ var obj = {};
82
+
83
+ if (xml.nodeType == 1) { // element
84
+ // do attributes
85
+ if (xml.attributes.length > 0) {
86
+ obj["@attributes"] = {};
87
+ for (var j = 0; j < xml.attributes.length; j++) {
88
+ var attribute = xml.attributes.item(j);
89
+ obj["@attributes"][attribute.nodeName] = attribute.nodeValue;
90
+ }
91
+ }
92
+ } else if (xml.nodeType == 3) { // text
93
+ obj = xml.nodeValue;
94
+ }
95
+
96
+ // do children
97
+ if (xml.hasChildNodes()) {
98
+ for(var i = 0; i < xml.childNodes.length; i++) {
99
+ var item = xml.childNodes.item(i);
100
+ var nodeName = item.nodeName;
101
+ if (typeof(obj[nodeName]) == "undefined") {
102
+ obj[nodeName] = this.xmlToJson(item);
103
+ } else {
104
+ if (typeof(obj[nodeName].push) == "undefined") {
105
+ var old = obj[nodeName];
106
+ obj[nodeName] = [];
107
+ obj[nodeName].push(old);
108
+ }
109
+ obj[nodeName].push(this.xmlToJson(item));
110
+ }
111
+ }
112
+ }
113
+ return obj;
114
+ };
115
+
116
+ BlogFetcher.prototype.trimToLength = function(str, max) {
117
+ if (str.length > max) {
118
+ return jQuery.trim(str).substring(0, max).split(' ').slice(0, -1).join(' ') + '...';
119
+ }
120
+ else {
121
+ return str;
122
+ }
123
+ };
124
+
125
+ return BlogFetcher;
126
+
127
+ })();
128
+
129
+ $(function() {
130
+ return window.Heroku.latestBlog = new Heroku.BlogFetcher('#more-blog');
131
+ });
@@ -10,6 +10,5 @@
10
10
  // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11
11
  // about supported directives.
12
12
  //
13
- //= require jquery
13
+ //= require heroku-namespace
14
14
  //= require_tree .
15
- //= require newsletter
@@ -0,0 +1 @@
1
+ var Heroku = Heroku || {};
@@ -0,0 +1,359 @@
1
+ /**
2
+ * lscache library
3
+ * Copyright (c) 2011, Pamela Fox
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ /* jshint undef:true, browser:true, node:true */
19
+ /* global define */
20
+
21
+ (function (root, factory) {
22
+ if (typeof define === 'function' && define.amd) {
23
+ // AMD. Register as an anonymous module.
24
+ define([], factory);
25
+ } else if (typeof module !== "undefined" && module.exports) {
26
+ // CommonJS/Node module
27
+ module.exports = factory();
28
+ } else {
29
+ // Browser globals
30
+ root.lscache = factory();
31
+ }
32
+ }(this, function () {
33
+
34
+ // Prefix for all lscache keys
35
+ var CACHE_PREFIX = 'lscache-';
36
+
37
+ // Suffix for the key name on the expiration items in localStorage
38
+ var CACHE_SUFFIX = '-cacheexpiration';
39
+
40
+ // expiration date radix (set to Base-36 for most space savings)
41
+ var EXPIRY_RADIX = 10;
42
+
43
+ // time resolution in minutes
44
+ var EXPIRY_UNITS = 60 * 1000;
45
+
46
+ // ECMAScript max Date (epoch + 1e8 days)
47
+ var MAX_DATE = Math.floor(8.64e15/EXPIRY_UNITS);
48
+
49
+ var cachedStorage;
50
+ var cachedJSON;
51
+ var cacheBucket = '';
52
+ var warnings = false;
53
+
54
+ // Determines if localStorage is supported in the browser;
55
+ // result is cached for better performance instead of being run each time.
56
+ // Feature detection is based on how Modernizr does it;
57
+ // it's not straightforward due to FF4 issues.
58
+ // It's not run at parse-time as it takes 200ms in Android.
59
+ function supportsStorage() {
60
+ var key = '__lscachetest__';
61
+ var value = key;
62
+
63
+ if (cachedStorage !== undefined) {
64
+ return cachedStorage;
65
+ }
66
+
67
+ try {
68
+ setItem(key, value);
69
+ removeItem(key);
70
+ cachedStorage = true;
71
+ } catch (e) {
72
+ if (isOutOfSpace(e)) { // If we hit the limit, then it means we have support,
73
+ cachedStorage = true; // just maxed it out and even the set test failed.
74
+ } else {
75
+ cachedStorage = false;
76
+ }
77
+ }
78
+ return cachedStorage;
79
+ }
80
+
81
+ // Check to set if the error is us dealing with being out of space
82
+ function isOutOfSpace(e) {
83
+ if (e && e.name === 'QUOTA_EXCEEDED_ERR' ||
84
+ e.name === 'NS_ERROR_DOM_QUOTA_REACHED' ||
85
+ e.name === 'QuotaExceededError') {
86
+ return true;
87
+ }
88
+ return false;
89
+ }
90
+
91
+ // Determines if native JSON (de-)serialization is supported in the browser.
92
+ function supportsJSON() {
93
+ /*jshint eqnull:true */
94
+ if (cachedJSON === undefined) {
95
+ cachedJSON = (window.JSON != null);
96
+ }
97
+ return cachedJSON;
98
+ }
99
+
100
+ /**
101
+ * Returns a string where all RegExp special characters are escaped with a \.
102
+ * @param {String} text
103
+ * @return {string}
104
+ */
105
+ function escapeRegExpSpecialCharacters(text) {
106
+ return text.replace(/[[\]{}()*+?.\\^$|]/g, '\\$&');
107
+ }
108
+
109
+ /**
110
+ * Returns the full string for the localStorage expiration item.
111
+ * @param {String} key
112
+ * @return {string}
113
+ */
114
+ function expirationKey(key) {
115
+ return key + CACHE_SUFFIX;
116
+ }
117
+
118
+ /**
119
+ * Returns the number of minutes since the epoch.
120
+ * @return {number}
121
+ */
122
+ function currentTime() {
123
+ return Math.floor((new Date().getTime())/EXPIRY_UNITS);
124
+ }
125
+
126
+ /**
127
+ * Wrapper functions for localStorage methods
128
+ */
129
+
130
+ function getItem(key) {
131
+ return localStorage.getItem(CACHE_PREFIX + cacheBucket + key);
132
+ }
133
+
134
+ function setItem(key, value) {
135
+ // Fix for iPad issue - sometimes throws QUOTA_EXCEEDED_ERR on setItem.
136
+ localStorage.removeItem(CACHE_PREFIX + cacheBucket + key);
137
+ localStorage.setItem(CACHE_PREFIX + cacheBucket + key, value);
138
+ }
139
+
140
+ function removeItem(key) {
141
+ localStorage.removeItem(CACHE_PREFIX + cacheBucket + key);
142
+ }
143
+
144
+ function eachKey(fn) {
145
+ var prefixRegExp = new RegExp('^' + CACHE_PREFIX + escapeRegExpSpecialCharacters(cacheBucket) + '(.*)');
146
+ // Loop in reverse as removing items will change indices of tail
147
+ for (var i = localStorage.length-1; i >= 0 ; --i) {
148
+ var key = localStorage.key(i);
149
+ key = key && key.match(prefixRegExp);
150
+ key = key && key[1];
151
+ if (key && key.indexOf(CACHE_SUFFIX) < 0) {
152
+ fn(key, expirationKey(key));
153
+ }
154
+ }
155
+ }
156
+
157
+ function flushItem(key) {
158
+ var exprKey = expirationKey(key);
159
+
160
+ removeItem(key);
161
+ removeItem(exprKey);
162
+ }
163
+
164
+ function flushExpiredItem(key) {
165
+ var exprKey = expirationKey(key);
166
+ var expr = getItem(exprKey);
167
+
168
+ if (expr) {
169
+ var expirationTime = parseInt(expr, EXPIRY_RADIX);
170
+
171
+ // Check if we should actually kick item out of storage
172
+ if (currentTime() >= expirationTime) {
173
+ removeItem(key);
174
+ removeItem(exprKey);
175
+ return true;
176
+ }
177
+ }
178
+ }
179
+
180
+ function warn(message, err) {
181
+ if (!warnings) return;
182
+ if (!('console' in window) || typeof window.console.warn !== 'function') return;
183
+ window.console.warn("lscache - " + message);
184
+ if (err) window.console.warn("lscache - The error was: " + err.message);
185
+ }
186
+
187
+ var lscache = {
188
+ /**
189
+ * Stores the value in localStorage. Expires after specified number of minutes.
190
+ * @param {string} key
191
+ * @param {Object|string} value
192
+ * @param {number} time
193
+ */
194
+ set: function(key, value, time) {
195
+ if (!supportsStorage()) return;
196
+
197
+ // If we don't get a string value, try to stringify
198
+ // In future, localStorage may properly support storing non-strings
199
+ // and this can be removed.
200
+ if (typeof value !== 'string') {
201
+ if (!supportsJSON()) return;
202
+ try {
203
+ value = JSON.stringify(value);
204
+ } catch (e) {
205
+ // Sometimes we can't stringify due to circular refs
206
+ // in complex objects, so we won't bother storing then.
207
+ return;
208
+ }
209
+ }
210
+
211
+ try {
212
+ setItem(key, value);
213
+ } catch (e) {
214
+ if (isOutOfSpace(e)) {
215
+ // If we exceeded the quota, then we will sort
216
+ // by the expire time, and then remove the N oldest
217
+ var storedKeys = [];
218
+ var storedKey;
219
+ eachKey(function(key, exprKey) {
220
+ var expiration = getItem(exprKey);
221
+ if (expiration) {
222
+ expiration = parseInt(expiration, EXPIRY_RADIX);
223
+ } else {
224
+ // TODO: Store date added for non-expiring items for smarter removal
225
+ expiration = MAX_DATE;
226
+ }
227
+ storedKeys.push({
228
+ key: key,
229
+ size: (getItem(key) || '').length,
230
+ expiration: expiration
231
+ });
232
+ });
233
+ // Sorts the keys with oldest expiration time last
234
+ storedKeys.sort(function(a, b) { return (b.expiration-a.expiration); });
235
+
236
+ var targetSize = (value||'').length;
237
+ while (storedKeys.length && targetSize > 0) {
238
+ storedKey = storedKeys.pop();
239
+ warn("Cache is full, removing item with key '" + key + "'");
240
+ flushItem(storedKey.key);
241
+ targetSize -= storedKey.size;
242
+ }
243
+ try {
244
+ setItem(key, value);
245
+ } catch (e) {
246
+ // value may be larger than total quota
247
+ warn("Could not add item with key '" + key + "', perhaps it's too big?", e);
248
+ return;
249
+ }
250
+ } else {
251
+ // If it was some other error, just give up.
252
+ warn("Could not add item with key '" + key + "'", e);
253
+ return;
254
+ }
255
+ }
256
+
257
+ // If a time is specified, store expiration info in localStorage
258
+ if (time) {
259
+ setItem(expirationKey(key), (currentTime() + time).toString(EXPIRY_RADIX));
260
+ } else {
261
+ // In case they previously set a time, remove that info from localStorage.
262
+ removeItem(expirationKey(key));
263
+ }
264
+ },
265
+
266
+ /**
267
+ * Retrieves specified value from localStorage, if not expired.
268
+ * @param {string} key
269
+ * @return {string|Object}
270
+ */
271
+ get: function(key) {
272
+ if (!supportsStorage()) return null;
273
+
274
+ // Return the de-serialized item if not expired
275
+ if (flushExpiredItem(key)) { return null; }
276
+
277
+ // Tries to de-serialize stored value if its an object, and returns the normal value otherwise.
278
+ var value = getItem(key);
279
+ if (!value || !supportsJSON()) {
280
+ return value;
281
+ }
282
+
283
+ try {
284
+ // We can't tell if its JSON or a string, so we try to parse
285
+ return JSON.parse(value);
286
+ } catch (e) {
287
+ // If we can't parse, it's probably because it isn't an object
288
+ return value;
289
+ }
290
+ },
291
+
292
+ /**
293
+ * Removes a value from localStorage.
294
+ * Equivalent to 'delete' in memcache, but that's a keyword in JS.
295
+ * @param {string} key
296
+ */
297
+ remove: function(key) {
298
+ if (!supportsStorage()) return;
299
+
300
+ flushItem(key);
301
+ },
302
+
303
+ /**
304
+ * Returns whether local storage is supported.
305
+ * Currently exposed for testing purposes.
306
+ * @return {boolean}
307
+ */
308
+ supported: function() {
309
+ return supportsStorage();
310
+ },
311
+
312
+ /**
313
+ * Flushes all lscache items and expiry markers without affecting rest of localStorage
314
+ */
315
+ flush: function() {
316
+ if (!supportsStorage()) return;
317
+
318
+ eachKey(function(key) {
319
+ flushItem(key);
320
+ });
321
+ },
322
+
323
+ /**
324
+ * Flushes expired lscache items and expiry markers without affecting rest of localStorage
325
+ */
326
+ flushExpired: function() {
327
+ if (!supportsStorage()) return;
328
+
329
+ eachKey(function(key) {
330
+ flushExpiredItem(key);
331
+ });
332
+ },
333
+
334
+ /**
335
+ * Appends CACHE_PREFIX so lscache will partition data in to different buckets.
336
+ * @param {string} bucket
337
+ */
338
+ setBucket: function(bucket) {
339
+ cacheBucket = bucket;
340
+ },
341
+
342
+ /**
343
+ * Resets the string being appended to CACHE_PREFIX so lscache will use the default storage behavior.
344
+ */
345
+ resetBucket: function() {
346
+ cacheBucket = '';
347
+ },
348
+
349
+ /**
350
+ * Sets whether to display warnings when an item is removed from the cache or not.
351
+ */
352
+ enableWarnings: function(enabled) {
353
+ warnings = enabled;
354
+ }
355
+ };
356
+
357
+ // Return the module
358
+ return lscache;
359
+ }));