...
 
Commits (135)
image: 0xacab.org:4567/riseuplabs/docker/crabgrass:latest image: 0xacab.org:4567/riseuplabs/docker/crabgrass:stretch_amd64
variables: variables:
MYSQL_ALLOW_EMPTY_PASSWORD: "yes" MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
...@@ -23,37 +23,14 @@ bundle_audit: ...@@ -23,37 +23,14 @@ bundle_audit:
bundle-audit check bundle-audit check
test: test:
stage: test
services:
- mysql:5.5
script: |
bundle exec rake create_a_secret
cp config/database.yml.example config/database.yml
bundle exec rake db:create
bundle exec rake db:schema:load
bundle exec rake cg:test:update_fixtures
bundle exec rake db:test:prepare
RAILS_ENV=test bundle exec rake db:fixtures:load
RAILS_ENV=test bundle exec rake ts:index ts:start
bundle exec rake
bundle exec rake cg:cleanup:all # test cleanup tasks
artifacts:
when: on_failure
paths:
- log/test.log
- tmp/*.log
- tmp/*.html
- tmp/*.png
test_stretch:
image: 0xacab.org:4567/riseuplabs/docker/crabgrass:stretch_amd64
stage: test stage: test
services: services:
- mariadb:10.1 - mariadb:10.1
variables: variables:
MYSQL_HOST: 'mariadb' MYSQL_HOST: 'mariadb'
RAILS_ENV: 'test'
COVERAGE: 'true'
script: | script: |
bundle exec rake create_a_secret
cp config/database.yml.example config/database.yml cp config/database.yml.example config/database.yml
bundle exec rake db:create bundle exec rake db:create
bundle exec rake db:schema:load bundle exec rake db:schema:load
...@@ -61,8 +38,9 @@ test_stretch: ...@@ -61,8 +38,9 @@ test_stretch:
bundle exec rake db:test:prepare bundle exec rake db:test:prepare
RAILS_ENV=test bundle exec rake db:fixtures:load RAILS_ENV=test bundle exec rake db:fixtures:load
RAILS_ENV=test bundle exec rake ts:index ts:start RAILS_ENV=test bundle exec rake ts:index ts:start
bundle exec rake bundle exec rails test test/functional test/unit test/integration extensions/pages
bundle exec rake cg:cleanup:all # test cleanup tasks echo 'Number of deprecations in the test logs:'
grep DEPRECATION log/test.log | wc -l
artifacts: artifacts:
when: on_failure when: on_failure
paths: paths:
......
AllCops: AllCops:
TargetRailsVersion: 5.1
TargetRubyVersion: 2.3
Exclude: Exclude:
- 'bin/*' - 'bin/*'
DisplayCopNames: true DisplayCopNames: true
...@@ -17,3 +19,8 @@ Metrics/LineLength: ...@@ -17,3 +19,8 @@ Metrics/LineLength:
- 'config/deploy.rb' - 'config/deploy.rb'
- 'config/permissions.rb' - 'config/permissions.rb'
- 'lib/**/*' - 'lib/**/*'
Style/ClassAndModuleChildren:
Exclude:
- 'app/helpers/**/*'
Style/FrozenStringLiteralComment:
AutoCorrect: false
SimpleCov.start 'rails' do
add_filter 'vendor/ruby' # Don't include bundled stuff
end
...@@ -20,7 +20,6 @@ addons: ...@@ -20,7 +20,6 @@ addons:
before_install: before_install:
- "bundle --version" - "bundle --version"
before_script: before_script:
- "bundle exec rake create_a_secret"
- "cp config/database.yml.example config/database.yml" - "cp config/database.yml.example config/database.yml"
- "bundle exec rake db:create" - "bundle exec rake db:create"
- "bundle exec rake db:schema:load" - "bundle exec rake db:schema:load"
......
source 'https://rubygems.org' source 'https://rubygems.org'
# ensure github urls use https rather than insecure git protocol. # ensure github urls use https rather than insecure git protocol.
git_source(:github) do |repo_name| git_source(:github) { |repo| "https://github.com/#{repo}.git" }
repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?('/')
"https://github.com/#{repo_name}.git"
end
## ##
# Core components # Core components
## ##
# Rails is the framework we use. # Rails is the framework we use.
# use the 4.2 series including all security fixes gem 'rails', '~> 5.2.3'
gem 'rails', '~> 4.2.11'
# Security updates # Security updates
# https://github.com/sparklemotion/nokogiri/issues/1785 #https://github.com/sparklemotion/nokogiri/issues/1892
gem 'nokogiri', '~> 1.8.5' gem 'nokogiri', '~> 1.10.3'
# Rake is rubys make... performing tasks # Rake is rubys make... performing tasks
# locking in to latest major to fix API # locking in to latest major to fix API
...@@ -25,13 +21,15 @@ gem 'rake', '~> 10.0', require: false ...@@ -25,13 +21,15 @@ gem 'rake', '~> 10.0', require: false
# Application preloader for faster start time # Application preloader for faster start time
gem 'spring', group: :development gem 'spring', group: :development
# translating strings for the user interface # reduces boot times through caching; required in config/boot.rb
gem 'bootsnap', '>= 1.1.0', require: false
# locking in to latest major to fix API # locking in to latest major to fix API
gem 'i18n', '~> 0.7' gem 'i18n', '~> 0.7'
# improved gem to access mysql database # improved gem to access mysql database
# locking in to latest major to fix API # locking in to latest major to fix API
gem 'mysql2', '~> 0.3.18' gem 'mysql2', '~> 0.5.2'
# parsing and generating JSON # parsing and generating JSON
# locking in to latest major to fix API # locking in to latest major to fix API
...@@ -39,8 +37,8 @@ gem 'json', '~> 1.8' ...@@ -39,8 +37,8 @@ gem 'json', '~> 1.8'
# Markup language that uses indent to indicate nesting # Markup language that uses indent to indicate nesting
# locking in to latest major to fix API # locking in to latest major to fix API
gem 'haml', '~> 4.0' gem 'haml', '~> 5.0'
gem 'haml-rails', '~> 0.9.0' gem 'haml-rails', '~> 1.0'
# Extendet scriptable CSS language # Extendet scriptable CSS language
# locking in to latest major to fix API # locking in to latest major to fix API
...@@ -48,29 +46,21 @@ gem 'sass' ...@@ -48,29 +46,21 @@ gem 'sass'
## ##
# Prototype - yes. we still use it. # Prototype - yes. we still use it.
# these will be replaced by jquery equivalents at some point: # we use a fork which is rails 5.x compatible
## # tests do not pass for this fork
gem 'prototype-rails', github: 'voxmedia/prototype-rails', ref: 'e385756cbabb5608d1eab47b6416cdd49613c73b'
# main part of prototype
# needs special branch for rails 4.2
gem 'prototype-rails', github: 'rails/prototype-rails', branch: '4.2'
# Full text search for the database # Full text search for the database
gem 'thinking-sphinx', '~> 3.4.2' gem 'thinking-sphinx', '~> 3.4.2'
# Enhanced Tagging lib. Used to tag pages # Enhanced Tagging lib. Used to tag pages
gem 'acts-as-taggable-on', '~> 4.0' gem 'acts-as-taggable-on', '~> 6.0'
# Rails 5 migration
## ##
# security updates
## # ActionView::Helpers::RecordTagHelper moved to external gem
# gem 'record_tag_helper', '~> 1.0'
# CVE-2018-16471
# Criticality: Unknown
# URL:
# https://groups.google.com/forum/#!topic/ruby-security-ann/NAalCee8n6o
# Title: Possible XSS vulnerability in Rack
gem 'rack', '~> 1.6.11'
## ##
# Upgrade pending # Upgrade pending
...@@ -95,10 +85,8 @@ gem 'pundit', '~> 1.1' ...@@ -95,10 +85,8 @@ gem 'pundit', '~> 1.1'
# Bcrypt for has_secure_password # Bcrypt for has_secure_password
gem 'bcrypt', '~> 3.1.7' gem 'bcrypt', '~> 3.1.7'
#
gem 'secure_headers', '~> 4.0.2' gem 'secure_headers', '~> 4.0.2'
# ? # ?
# locking in to latest major to fix API # locking in to latest major to fix API
gem 'http_accept_language', '~> 2.0' gem 'http_accept_language', '~> 2.0'
...@@ -144,7 +132,7 @@ gem 'greencloth', require: 'greencloth', ...@@ -144,7 +132,7 @@ gem 'greencloth', require: 'greencloth',
# media upload post processing has it's own repo # media upload post processing has it's own repo
# version is rather strict for now as api may still change. # version is rather strict for now as api may still change.
gem 'crabgrass_media', '~> 0.2.1', require: 'media' gem 'crabgrass_media', '~> 0.3.1', require: 'media'
## ##
## not required, but a really good idea ## not required, but a really good idea
...@@ -181,6 +169,7 @@ group :production do ...@@ -181,6 +169,7 @@ group :production do
# runs independendly - so no version restriction for now # runs independendly - so no version restriction for now
# TODO: check if we want this or nodejs # TODO: check if we want this or nodejs
gem 'therubyracer' gem 'therubyracer'
# gem 'mini_racer', platforms: :ruby # new default in Rails 5.2
end end
group :production, :development do group :production, :development do
...@@ -204,6 +193,8 @@ group :test, :development do ...@@ -204,6 +193,8 @@ group :test, :development do
gem 'byebug' gem 'byebug'
end end
gem 'web-console', group: :development
group :test, :ci do group :test, :ci do
## ##
## TESTS ## TESTS
...@@ -212,7 +203,11 @@ group :test, :ci do ...@@ -212,7 +203,11 @@ group :test, :ci do
gem 'factory_bot_rails' gem 'factory_bot_rails'
gem 'faker', '~> 1.0.0' gem 'faker', '~> 1.0.0'
gem 'minitest', require: false # temporary fix for minitest 5.11 issue
gem 'minitest', '~>5.10.3', require: false
# contains helper methods like assigns and assert_template
gem 'rails-controller-testing'
## ##
## INTEGRATION TESTS ## INTEGRATION TESTS
......
This diff is collapsed.
...@@ -68,7 +68,6 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| ...@@ -68,7 +68,6 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.provision 'Install project dependencies', type: 'shell', privileged: false, inline: <<-SHELL config.vm.provision 'Install project dependencies', type: 'shell', privileged: false, inline: <<-SHELL
cd /vagrant/ cd /vagrant/
bundle install bundle install
rake create_a_secret
SHELL SHELL
config.vm.provision 'Connect MySQL DB', type: 'shell', privileged: false, inline: <<-SHELL config.vm.provision 'Connect MySQL DB', type: 'shell', privileged: false, inline: <<-SHELL
......
...@@ -2,6 +2,7 @@ Javascripts ...@@ -2,6 +2,7 @@ Javascripts
============================================== ==============================================
Javascript are served using the Sprockets gem for asset pipelining. Javascript are served using the Sprockets gem for asset pipelining.
We load all scripts by including application.js on all pages.
About these scripts About these scripts
---------------------------------------------- ----------------------------------------------
...@@ -20,19 +21,6 @@ app/javascripts/prototype.js ...@@ -20,19 +21,6 @@ app/javascripts/prototype.js
scriptaculous effects for prototype that we also use. scriptaculous effects for prototype that we also use.
Using scripts
----------------------------------------------
We currently load all scripts on all pages (e.g. there is nothing in
as_needed). If you want an 'as_needed' script that is only loaded for a
particular controller:
class MyController < ApplicationController
javascript :tasks, :wiki
javascript :extra, :action => :index
end
Stylesheets Stylesheets
============================================== ==============================================
......
...@@ -16,18 +16,18 @@ function cgAutocompleteEntities(id, url, opts) { ...@@ -16,18 +16,18 @@ function cgAutocompleteEntities(id, url, opts) {
rowRenderer: autoCompleteRowRenderer, rowRenderer: autoCompleteRowRenderer,
selectValue: autoCompleteSelectValue selectValue: autoCompleteSelectValue
}; };
if (opts) { Object.extend(options, opts); } if (opts) { Object.extend(options, opts); }
new Autocomplete(id, options, random_id); new Autocomplete(id, options, random_id);
}
function autoCompleteRowRenderer(value, re, data) { function autoCompleteRowRenderer(value, re, data) {
return "<p class='icon xsmall' style='background-image: url(/avatars/" + data + "/xsmall.jpg)'>" + value.replace(/^<em>(.*)<\/em>(<br\/>(.*))?$/gi, function(m, m1, m2, m3){return "<em>" + Autocomplete.highlight(m1,re) + "</em>" + (m3 ? "<br/>" + Autocomplete.highlight(m3, re) : "")}) + "</p>"; return "<p class='icon xsmall' style='background-image: url(/avatars/" + data + "/xsmall.jpg)'>" + value.replace(/^<em>(.*)<\/em>(<br\/>(.*))?$/gi, function(m, m1, m2, m3){return "<em>" + Autocomplete.highlight(m1,re) + "</em>" + (m3 ? "<br/>" + Autocomplete.highlight(m3, re) : "")}) + "</p>";
} }
function autoCompleteSelectValue(value){ function autoCompleteSelectValue(value){
// if there are two rows pick the second one // if there are two rows pick the second one
row = value.replace(/.*<br\/>(.*)/g,'$1'); row = value.replace(/.*<br\/>(.*)/g,'$1');
// encode text without the tags // encode text without the tags
return row.replace(/<[^>]*>/g,''); return row.replace(/<[^>]*>/g,'');
// return encodeURIComponent(row.replace(/<[^>]*>/g,'')); // return encodeURIComponent(row.replace(/<[^>]*>/g,''));
}
} }
...@@ -48,16 +48,6 @@ function replaceClassName(element, old_class, new_class) { ...@@ -48,16 +48,6 @@ function replaceClassName(element, old_class, new_class) {
element.removeClassName(old_class); element.addClassName(new_class) element.removeClassName(old_class); element.addClassName(new_class)
} }
//
// replaces an element with a new one if it is empty.
//
function replaceIfEmpty(selector, newElement) {
var el = $$(selector).first()
if (el && el.empty()) {
el.replace(newElement);
}
}
// //
// EVENTS // EVENTS
// //
...@@ -102,40 +92,3 @@ var LocationHash = { ...@@ -102,40 +92,3 @@ var LocationHash = {
this.polling = true; this.polling = true;
} }
} }
//
// split panel
//
function activatePanelRow(row_id) {
// reset styles
$$('.panel_right .row').invoke('hide');
$$('.panel_arrow').invoke('hide');
$$('.panel_left .row').invoke('removeClassName', 'active');
if (row_id) {
// highlight left panel row
$('panel_left_'+row_id).addClassName('active');
var halfHeight = $('panel_left_'+row_id).getHeight() / 2 + "px";
var borderWidthStr = "#{top} #{right} #{bottom} #{left}".interpolate({top: halfHeight, right:"0px", bottom: halfHeight, left:"10px"});
$('panel_arrow_'+row_id).setStyle({borderWidth: borderWidthStr, display: 'block'});
// position and show right panel row
var offset = $('panel_left_'+row_id).offsetTop + 'px';
$$('.panel_right').first().setStyle({paddingTop:offset})
$('panel_right_'+row_id).show();
}
}
//
// sliding list
//
// left and right contain these keys:
// path -- the url path of the panel. this is used both for history
// and loading content via ajax.
// domid -- dom id of the element to update
//
function activateSlidingRow(left, right) {
History.replaceState({slide_right:'sliding-list', update:left}, null, left.path);
History.pushState({slide_left:'sliding-list', update:right}, null, right.path);
}
//
// Busy cursor
//
// When a ajax request is pending, this will change the cursor to be a busy cursor.
// It depends on this css:
//
// html.busy, html.busy * {
// cursor: wait !important;
// }
//
// source: http://postpostmodern.com/instructional/global-ajax-cursor-change/
//
// NOTE: currently disabled. This is really cool, but some ajax queries
// cause activeRequestCount to keep incrementing. Until that is fixed,
// this must be disabled.
//
// in prototype:
// Ajax.Responders.register({
// onCreate: function() {
// if (Ajax.activeRequestCount > 0) {
// $$('html')[0].addClassName('busy');
// }
// },
// onComplete: function() {
// console.log("Ajax.activeRequestCount " + Ajax.activeRequestCount);
// if (Ajax.activeRequestCount == 0) {
// $$('html')[0].removeClassName('busy');
// }
// }
// });
// in jquery:
// function globalAjaxCursorChange() {
// $("html").bind("ajaxStart", function() {
// $(this).addClass('busy');
// }).bind("ajaxStop", function() {
// $(this).removeClass('busy');
// });
// }
\ No newline at end of file
//
// a modification of Effect.Move to support percentage offsets.
//
Effect.MoveByPercent = Class.create(Effect.Base, {
initialize: function(element) {
this.element = $(element);
if (!this.element) throw(Effect._elementDoesNotExistError);
var options = Object.extend({x:0, y:0}, arguments[1] || { });
this.start(options);
},
setup: function() {
this.element.makePositioned();
this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
this.originalTop = parseFloat(this.element.getStyle('top') || '0');
},
update: function(position) {
this.element.setStyle({
left: (this.options.x * position + this.originalLeft).round() + '%',
top: (this.options.y * position + this.originalTop).round() + '%'
});
}
});
...@@ -91,22 +91,6 @@ function linkToggle(link, element, functn) { ...@@ -91,22 +91,6 @@ function linkToggle(link, element, functn) {
} }
} }
// Finds all elements with the selector and removes the
// 'disabled' class.
// Options:
// * selector: selects the elements to apply on
// * disable: if set class 'disabled' will be set instead
// function enableAll(selector, disable) {
// var affectedElements = $$(selector);
// affectedElements.each(function(e) {
// if (disable) {
// e.addClassName('disabled');
// } else {
// e.removeClassName('disabled');
// }
// });
// }
// //
// Checks if this element is in an disabled part of the DOM // Checks if this element is in an disabled part of the DOM
// Can be used as a condition for onclick actions that should // Can be used as a condition for onclick actions that should
......
// //
// // crabgrass pushState history support.
// //
// History.Adapter.bind(window,'statechange',function() {
// var state = History.getState();
// console.log('history state change: ' + JSON.stringify({url:state.url, data:state.data}))
// if (state.data) {
// process_history_state_change(state);
// }
// });
// function process_history_state_change(state) {
// var data = state.data;
// //
// // update a dom element with an ajax request
// //
// if (data.update && $(data.update.domid)) {
// new Ajax.Updater(data.update.domid, data.update.path, {
// asynchronous:true, evalScripts:true, method:'get'
// //parameters: 'authenticity_token=' + encodeURIComponent(data.token)
// });
// }
// //
// // hide and show a dom element
// //
// //if (data.hide && $(data.hide)) { $(data.hide).hide(); }
// //if (data.show && $(data.show)) { $(data.show).show(); }
// //
// // slide an dom element around
// //
// if (data.slide_left && $(data.slide_left)) {
// new Effect.MoveByPercent(data.slide_left, {duration: 0.5, x:-100});
// }
// if (data.slide_right && $(data.slide_right)) {
// new Effect.MoveByPercent(data.slide_right, {duration: 0.5, x:100});
// }
// }
A library to make HTML5 window.history work better, and also fallback
gracefully for HTML4 browsers.
https://github.com/balupton/History.js/
It is a little heavyweight, and includes a bunch of features we don't
need -- like storing data with history. For this reason, I created
json2-dummy.js, which tricks history.js into not storing data.
To update the source files in this directory:
cd app/assets/javascripts/libraries/history
curl https://nodeload.github.com/balupton/History.js/tarball/master | tar -vxz --transform 's/.*\/scripts\/uncompressed\///' --overwrite --wildcards '*/uncompressed/*.js'
rm history.adapter.jquery.js
rm history.adapter.mootools.js
rm history.adapter.right.js
rm history.adapter.zepto.js
There are two options we have for history.js support: html5 only or html5+html4.
For html5 only support, bundle these files:
history/history.adapter.native.js
history/history.js
For html4+html5 support, bundle these files (in this order):
history/json2.js
history/history.adapter.native.js
history/history.html4.js
history/history.js
See https://github.com/balupton/history.js/blob/master/buildr.coffee for the build rules.
NOTE: if/when we switch to jquery, replace native adapter with jquery adaptor.
/**
* History.js Native Adapter
* @author Benjamin Arthur Lupton <contact@balupton.com>
* @copyright 2010-2011 Benjamin Arthur Lupton <contact@balupton.com>
* @license New BSD License <http://creativecommons.org/licenses/BSD/>
*/
// Closure
(function(window,undefined){
"use strict";
// Localise Globals
var History = window.History = window.History||{};
// Check Existence
if ( typeof History.Adapter !== 'undefined' ) {
throw new Error('History.js Adapter has already been loaded...');
}
// Add the Adapter
History.Adapter = {
/**
* History.Adapter.handlers[uid][eventName] = Array
*/
handlers: {},
/**
* History.Adapter._uid
* The current element unique identifier
*/
_uid: 1,
/**
* History.Adapter.uid(element)
* @param {Element} element
* @return {String} uid
*/
uid: function(element){
return element._uid || (element._uid = History.Adapter._uid++);
},
/**
* History.Adapter.bind(el,event,callback)
* @param {Element} element
* @param {String} eventName - custom and standard events
* @param {Function} callback
* @return
*/
bind: function(element,eventName,callback){
// Prepare
var uid = History.Adapter.uid(element);
// Apply Listener
History.Adapter.handlers[uid] = History.Adapter.handlers[uid] || {};
History.Adapter.handlers[uid][eventName] = History.Adapter.handlers[uid][eventName] || [];
History.Adapter.handlers[uid][eventName].push(callback);
// Bind Global Listener
element['on'+eventName] = (function(element,eventName){
return function(event){
History.Adapter.trigger(element,eventName,event);
};
})(element,eventName);
},
/**
* History.Adapter.trigger(el,event)
* @param {Element} element
* @param {String} eventName - custom and standard events
* @param {Object} event - a object of event data
* @return
*/
trigger: function(element,eventName,event){
// Prepare
event = event || {};
var uid = History.Adapter.uid(element),
i,n;
// Apply Listener
History.Adapter.handlers[uid] = History.Adapter.handlers[uid] || {};
History.Adapter.handlers[uid][eventName] = History.Adapter.handlers[uid][eventName] || [];
// Fire Listeners
for ( i=0,n=History.Adapter.handlers[uid][eventName].length; i<n; ++i ) {
History.Adapter.handlers[uid][eventName][i].apply(this,[event]);
}
},
/**
* History.Adapter.extractEventData(key,event,extra)
* @param {String} key - key for the event data to extract
* @param {String} event - custom and standard events
* @return {mixed}
*/
extractEventData: function(key,event){
var result = (event && event[key]) || undefined;
return result;
},
/**
* History.Adapter.onDomLoad(callback)
* @param {Function} callback
* @return
*/
onDomLoad: function(callback) {
var timeout = window.setTimeout(function(){
callback();
},2000);
window.onload = function(){
clearTimeout(timeout);
callback();
};
}
};
// Try and Initialise History
if ( typeof History.init !== 'undefined' ) {
History.init();
}
})(window);
This diff is collapsed.
//
// This is a dummy json2.js with the same API, but it doesn't do anything.
// Created because I don't want or need the functionality it provides to history.js
//
if (!window.JSON) {
window.JSON = {};
}
(function () {
if (typeof JSON.stringify !== 'function') {
JSON.stringify = function (value, replacer, space) {return "{}" }
}
if (typeof JSON.parse !== 'function') {
JSON.parse = function (text, reviver) { return {} }
}
}());
This diff is collapsed.
//= require ./shims/classList.js
/*
* classList.js: Cross-browser full element.classList implementation.
* 1.2.20171210
*
* By Eli Grey, http://eligrey.com
* License: Dedicated to the public domain.
* See https://github.com/eligrey/classList.js/blob/master/LICENSE.md
*/
/*global self, document, DOMException */
/*! @source http://purl.eligrey.com/github/classList.js/blob/master/classList.js */
if ("document" in self) {
// Full polyfill for browsers with no classList support
// Including IE < Edge missing SVGElement.classList
if (
!("classList" in document.createElement("_"))
|| document.createElementNS
&& !("classList" in document.createElementNS("http://www.w3.org/2000/svg","g"))
) {
(function (view) {
"use strict";
if (!('Element' in view)) return;
var
classListProp = "classList"
, protoProp = "prototype"
, elemCtrProto = view.Element[protoProp]
, objCtr = Object
, strTrim = String[protoProp].trim || function () {
return this.replace(/^\s+|\s+$/g, "");
}
, arrIndexOf = Array[protoProp].indexOf || function (item) {
var
i = 0
, len = this.length
;
for (; i < len; i++) {
if (i in this && this[i] === item) {
return i;
}
}
return -1;
}
// Vendors: please allow content code to instantiate DOMExceptions
, DOMEx = function (type, message) {
this.name = type;
this.code = DOMException[type];
this.message = message;
}
, checkTokenAndGetIndex = function (classList, token) {
if (token === "") {
throw new DOMEx(
"SYNTAX_ERR"
, "The token must not be empty."
);
}
if (/\s/.test(token)) {
throw new DOMEx(
"INVALID_CHARACTER_ERR"
, "The token must not contain space characters."
);
}
return arrIndexOf.call(classList, token);
}
, ClassList = function (elem) {
var
trimmedClasses = strTrim.call(elem.getAttribute("class") || "")
, classes = trimmedClasses ? trimmedClasses.split(/\s+/) : []
, i = 0
, len = classes.length
;
for (; i < len; i++) {
this.push(classes[i]);
}
this._updateClassName = function () {
elem.setAttribute("class", this.toString());
};
}
, classListProto = ClassList[protoProp] = []
, classListGetter = function () {
return new ClassList(this);
}
;
// Most DOMException implementations don't allow calling DOMException's toString()
// on non-DOMExceptions. Error's toString() is sufficient here.
DOMEx[protoProp] = Error[protoProp];
classListProto.item = function (i) {
return this[i] || null;
};
classListProto.contains = function (token) {
return ~checkTokenAndGetIndex(this, token + "");
};
classListProto.add = function () {
var
tokens = arguments
, i = 0
, l = tokens.length
, token
, updated = false
;
do {
token = tokens[i] + "";
if (!~checkTokenAndGetIndex(this, token)) {
this.push(token);
updated = true;
}
}
while (++i < l);
if (updated) {
this._updateClassName();
}
};
classListProto.remove = function () {
var
tokens = arguments
, i = 0
, l = tokens.length
, token
, updated = false
, index
;
do {
token = tokens[i] + "";
index = checkTokenAndGetIndex(this, token);
while (~index) {
this.splice(index, 1);
updated = true;
index = checkTokenAndGetIndex(this, token);
}
}
while (++i < l);
if (updated) {
this._updateClassName();
}
};
classListProto.toggle = function (token, force) {
var
result = this.contains(token)
, method = result ?
force !== true && "remove"
:
force !== false && "add"
;
if (method) {
this[method](token);
}
if (force === true || force === false) {
return force;
} else {
return !result;
}
};
classListProto.replace = function (token, replacement_token) {
var index = checkTokenAndGetIndex(token + "");
if (~index) {
this.splice(index, 1, replacement_token);
this._updateClassName();
}
}
classListProto.toString = function () {
return this.join(" ");
};
if (objCtr.defineProperty) {
var classListPropDesc = {
get: classListGetter
, enumerable: true
, configurable: true
};
try {
objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
} catch (ex) { // IE 8 doesn't support enumerable:true
// adding undefined to fight this issue https://github.com/eligrey/classList.js/issues/36
// modernie IE8-MSW7 machine has IE8 8.0.6001.18702 and is affected
if (ex.number === undefined || ex.number === -0x7FF5EC54) {
classListPropDesc.enumerable = false;
objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
}
}
} else if (objCtr[protoProp].__defineGetter__) {
elemCtrProto.__defineGetter__(classListProp, classListGetter);
}
}(self));
}
// There is full or partial native classList support, so just check if we need
// to normalize the add/remove and toggle APIs.
(function () {
"use strict";
var testElement = document.createElement("_");
testElement.classList.add("c1", "c2");
// Polyfill for IE 10/11 and Firefox <26, where classList.add and
// classList.remove exist but support only one argument at a time.
if (!testElement.classList.contains("c2")) {
var createMethod = function(method) {
var original = DOMTokenList.prototype[method];
DOMTokenList.prototype[method] = function(token) {
var i, len = arguments.length;
for (i = 0; i < len; i++) {
token = arguments[i];
original.call(this, token);
}
};
};
createMethod('add');
createMethod('remove');
}
testElement.classList.toggle("c3", false);
// Polyfill for IE 10 and Firefox <24, where classList.toggle does not
// support the second argument.
if (testElement.classList.contains("c3")) {
var _toggle = DOMTokenList.prototype.toggle;
DOMTokenList.prototype.toggle = function(token, force) {
if (1 in arguments && !this.contains(token) === !force) {
return force;
} else {
return _toggle.call(this, token);
}
};
}
// replace() polyfill
if (!("replace" in document.createElement("_").classList)) {
DOMTokenList.prototype.replace = function (token, replacement_token) {
var
tokens = this.toString().split(" ")
, index = tokens.indexOf(token + "")
;
if (~index) {
tokens = tokens.slice(index);
this.remove.apply(this, tokens);
this.add(replacement_token);
this.add.apply(this, tokens.slice(1));
}
}
}
testElement = null;
}());
}
\ No newline at end of file
...@@ -101,7 +101,7 @@ class AccountsController < ApplicationController ...@@ -101,7 +101,7 @@ class AccountsController < ApplicationController
user = User.find_by_email params[:email] user = User.find_by_email params[:email]
if user if user
token = User::Token.to_recover.create(user: user) token = User::Token.to_recover.create(user: user)
Mailer.forgot_password(token, mailer_options).deliver Mailer.forgot_password(token, mailer_options).deliver_now
end end
# this gives success even if there is no user, to not confirm that an email is in db # this gives success even if there is no user, to not confirm that an email is in db
...@@ -120,7 +120,7 @@ class AccountsController < ApplicationController ...@@ -120,7 +120,7 @@ class AccountsController < ApplicationController
@user.password = params[:new_password] @user.password = params[:new_password]
@user.password_confirmation = params[:password_confirmation] @user.password_confirmation = params[:password_confirmation]
if @user.save if @user.save
Mailer.reset_password(@user, mailer_options).deliver Mailer.reset_password(@user, mailer_options).deliver_now
@token.destroy @token.destroy
success :password_reset.t, :password_reset_ok_text.t, :nofade success :password_reset.t, :password_reset_ok_text.t, :nofade
redirect_to root_path redirect_to root_path
...@@ -134,7 +134,7 @@ class AccountsController < ApplicationController ...@@ -134,7 +134,7 @@ class AccountsController < ApplicationController
# confirms that the token is valid, returns false otherwise. # confirms that the token is valid, returns false otherwise.
# #
def confirm_token def confirm_token
@token = User::Token.to_recover.active.find_by_param(params[:token]) @token = User::Token.to_recover.active.find_by_param(params.fetch(:token))
if @token.present? if @token.present?
@user = @token.user @user = @token.user
else else
......