diff --git a/.ruby-version b/.ruby-version
new file mode 100644
index 000000000..227cea215
--- /dev/null
+++ b/.ruby-version
@@ -0,0 +1 @@
+2.0.0
diff --git a/Gemfile b/Gemfile
index 1b56dd20f..0946f4a0e 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,7 +1,5 @@
source 'http://rubygems.org'
-ruby '2.2.1'
-
gem 'rails', '~> 4.1.9'
gem 'sass-rails', '~> 4.0.3'
gem 'uglifier', '>= 2.5.3'
diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js
index be81fa3ea..824a7c8db 100644
--- a/app/assets/javascripts/application.js
+++ b/app/assets/javascripts/application.js
@@ -11,6 +11,7 @@
//= require backbone
//= require backbone.rails
//= require Markdown.Converter
+//= require clipboard
//= require_tree ./templates
//= require_tree ./mixins
//= require_tree ./models
diff --git a/app/assets/javascripts/models/story.js b/app/assets/javascripts/models/story.js
index b88c690e0..16fe0248c 100644
--- a/app/assets/javascripts/models/story.js
+++ b/app/assets/javascripts/models/story.js
@@ -22,6 +22,10 @@ Fulcrum.Story = Backbone.Model.extend({
},
matchesSearch: function(params) {
+ if (params.id) {
+ return this.get('id').toString() === params.id;
+ }
+
var matchesTags = true;
var matchesText = true;
diff --git a/app/assets/javascripts/views/search_view.js b/app/assets/javascripts/views/search_view.js
index 98870e826..515c56681 100644
--- a/app/assets/javascripts/views/search_view.js
+++ b/app/assets/javascripts/views/search_view.js
@@ -5,6 +5,7 @@ if (typeof Fulcrum == 'undefined') {
Fulcrum.SearchView = Backbone.View.extend({
TAGS_REGEX: /tags:([a-z,]*)/i,
+ ID_REGEX: /^\s*#([0-9]+)/,
template: JST['templates/search'],
@@ -19,12 +20,16 @@ Fulcrum.SearchView = Backbone.View.extend({
params = _.queryParams(params);
var search = '';
- if (params.tags) {
- search = 'tags:' + params.tags + ' ';
- }
+ if (params.id) {
+ search = '#' + params.id;
+ } else {
+ if (params.tags) {
+ search = 'tags:' + params.tags + ' ';
+ }
- if (params.text) {
- search += params.text;
+ if (params.text) {
+ search += params.text;
+ }
}
that.input.val(search);
@@ -40,19 +45,25 @@ Fulcrum.SearchView = Backbone.View.extend({
search: function() {
var params = this.input.val();
- var tags = this.TAGS_REGEX.exec(params);
+ var id = this.ID_REGEX.exec(params);
var options = {};
- if (tags) {
- options.tags = tags[1];
- params = params.replace(tags[0], '');
- }
+ if (id) {
+ options.id = id[1];
+ } else {
+ var tags = this.TAGS_REGEX.exec(params);
- if (params) {
- options.text = params.trim();
+ if (tags) {
+ options.tags = tags[1];
+ params = params.replace(tags[0], '');
+ }
+
+ if (params) {
+ options.text = params.trim();
+ }
}
- if (params || tags) {
+ if (params || tags || id) {
Fulcrum.appRouter.navigate('search?' + $.param(options), {trigger: true});
} else {
Fulcrum.appRouter.navigate('/', {trigger: true});
diff --git a/app/assets/javascripts/views/story_view.js b/app/assets/javascripts/views/story_view.js
index 3bb1b0019..c4f920d56 100644
--- a/app/assets/javascripts/views/story_view.js
+++ b/app/assets/javascripts/views/story_view.js
@@ -212,6 +212,9 @@ Fulcrum.StoryView = Fulcrum.FormView.extend({
},
cancelEdit: function() {
+ if (this.clipboard)
+ this.clipboard.destroy();
+
this.model.set({editing: false});
// If the model was edited, but the edits were deemed invalid by the
@@ -295,6 +298,42 @@ Fulcrum.StoryView = Fulcrum.FormView.extend({
})
);
+ if (this.model.id) {
+ var that = this;
+
+ this.$el.append(
+ this.makeFormControl(function (div) {
+ var copyButton = $('');
+
+ that.clipboard = new Clipboard(copyButton[0], {
+ text: function () {
+ return '#' + that.model.id;
+ }
+ });
+
+ that.clipboard.on('success', function() {
+ copyButton.tooltip({
+ title: I18n.t('copied'),
+ placement: 'bottom',
+ trigger: 'manual',
+ delay: { show: 500 }
+ });
+
+ copyButton.tooltip('show');
+
+ setTimeout(function() {
+ copyButton.tooltip('destroy');
+ }, 2000);
+ });
+
+ $(div)
+ .addClass('story-id')
+ .append(copyButton)
+ .append('#' + this.model.id + '');
+ })
+ );
+ }
+
this.$el.append(
this.makeFormControl(function(div) {
$(div).append(this.textField("title", {
@@ -411,6 +450,9 @@ Fulcrum.StoryView = Fulcrum.FormView.extend({
disableForm: function() {
this.$el.find('input,select,textarea').attr('disabled', 'disabled');
this.$el.find('a.collapse,a.expand').removeClass(/icons-/).addClass('icons-throbber');
+
+ if (this.clipboard)
+ this.clipboard.destroy();
},
enableForm: function() {
diff --git a/app/assets/stylesheets/screen.css.scss b/app/assets/stylesheets/screen.css.scss
index 499da3b26..d8551a9ff 100644
--- a/app/assets/stylesheets/screen.css.scss
+++ b/app/assets/stylesheets/screen.css.scss
@@ -293,6 +293,27 @@ div.story-icons {
div.story-controls {
padding: 10px 0;
}
+
+div.story-id {
+ background-color: #ccc;
+ border-radius: 2px;
+ border: 1px solid #828282;
+ margin-bottom: 8px;
+
+ .id-btn {
+ background-color: #5e5e5e;
+ border: 0;
+ color: white;
+ cursor: pointer;
+ display: inline-block;
+ font-weight: bold;
+ margin-right: 5px;
+ padding: 2px 4px;
+ user-select: none;
+ -webkit-user-select: none;
+ }
+}
+
div.story-title {
margin-left: 50px;
}
diff --git a/config/locales/el.yml b/config/locales/el.yml
index b75b56a38..1b62fe1b7 100644
--- a/config/locales/el.yml
+++ b/config/locales/el.yml
@@ -231,6 +231,7 @@ el:
rejected: απορριφθούν
delivered: παραδοθούν
search: Αναζήτηση ...
+ copied: Αντιγράφεται!
author unknown: "αγνωστος"
add story: "νέα ιστορία"
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 669b7aabc..187d837dc 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -21,6 +21,7 @@ en:
saving: "Saving ..."
expand: "Expand"
search: "Search..."
+ copied: "Copied!"
author unknown: "Author Unknown"
add story: "Add story"
diff --git a/config/locales/es.yml b/config/locales/es.yml
index 981adbd40..c4e92a11d 100644
--- a/config/locales/es.yml
+++ b/config/locales/es.yml
@@ -10,6 +10,7 @@ es:
import: "Importa"
export: "Exporta"
search: "Busca..."
+ copied: "¡Copiado!"
author unknown: "Autor desconocido"
diff --git a/config/locales/ja.yml b/config/locales/ja.yml
index 75f16d347..e1f40dabc 100644
--- a/config/locales/ja.yml
+++ b/config/locales/ja.yml
@@ -21,6 +21,7 @@ ja:
saving: "保存中…"
expand: "広げる"
search: "検索..."
+ copied: "コピーしました!"
author unknown: "作者不明"
add story: "ストーリー追加"
diff --git a/config/locales/nl.yml b/config/locales/nl.yml
index 5b38675a1..20824cf45 100644
--- a/config/locales/nl.yml
+++ b/config/locales/nl.yml
@@ -18,6 +18,7 @@ nl:
saving: "Opslaan ..."
expand: "Uitvouwen"
search: "Zoeken ..."
+ copied: "Gekopieerd!"
author unknown: "Auteur Onbekend"
add story: "Voeg verhaal toe"
diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml
index 94b0ff9e0..956df4703 100644
--- a/config/locales/pt-BR.yml
+++ b/config/locales/pt-BR.yml
@@ -18,6 +18,7 @@ pt-BR:
saving: "Salvando ..."
expand: "Expandir"
search: "Pesquisa..."
+ copied: "Copiado!"
author unknown: "Autor Desconhecido"
add story: "Adicionar história"
diff --git a/vendor/assets/javascripts/clipboard.js b/vendor/assets/javascripts/clipboard.js
new file mode 100644
index 000000000..65ad89672
--- /dev/null
+++ b/vendor/assets/javascripts/clipboard.js
@@ -0,0 +1,745 @@
+/*!
+ * clipboard.js v1.5.5
+ * https://zenorocha.github.io/clipboard.js
+ *
+ * Licensed MIT © Zeno Rocha
+ */
+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Clipboard = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o