radiant-filter_toolbars-extension 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +35 -0
- data/Rakefile +109 -0
- data/cucumber.yml +1 -0
- data/features/support/env.rb +11 -0
- data/features/support/paths.rb +22 -0
- data/filter_toolbars_extension.rb +9 -0
- data/lib/radiant_filter_toolbars_extension.rb +6 -0
- data/lib/radiant_filter_toolbars_extension/controllers/admin/pages_controller.rb +24 -0
- data/lib/tasks/filter_toolbars_extension_tasks.rake +55 -0
- data/public/images/admin/filter_toolbars/icons.gif +0 -0
- data/public/javascripts/admin/filter_toolbars/control/livepipe.js +181 -0
- data/public/javascripts/admin/filter_toolbars/control/textarea.js +123 -0
- data/public/javascripts/admin/filter_toolbars/filter_toolbars.js +81 -0
- data/public/javascripts/admin/filter_toolbars/filters/markdown.js +57 -0
- data/public/javascripts/admin/filter_toolbars/filters/textile.js +52 -0
- data/public/stylesheets/sass/admin/filter_toolbars.scss +62 -0
- data/radiant-filter_toolbars-extension.gemspec +29 -0
- data/spec/spec.opts +6 -0
- data/spec/spec_helper.rb +36 -0
- metadata +89 -0
data/README.md
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
#Filter Toolbars
|
2
|
+
|
3
|
+
Adds a textile or markdown WYSIWYG filter to the admin textareas using Control.TextArea [http://livepipe.net/control/textarea]
|
4
|
+
|
5
|
+
If the Clipped asset manager extension is installed then the image button will launch the assets manager.
|
6
|
+
|
7
|
+
|
8
|
+
## TODO
|
9
|
+
|
10
|
+
- Disable toolbar when selecting <none>
|
11
|
+
- Refactor duplicated filter funtions
|
12
|
+
- Implement SmartyPants
|
13
|
+
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
|
17
|
+
This has only been tested on Radiant v1.0.0 +
|
18
|
+
|
19
|
+
Install as a gem :
|
20
|
+
|
21
|
+
```
|
22
|
+
gem install radiant-filter_toolbars-extension
|
23
|
+
```
|
24
|
+
|
25
|
+
Include the gem in your environment.rb :
|
26
|
+
|
27
|
+
```
|
28
|
+
config.gem 'radiant-filter_toolbars-extension', :version => '~>1.0.0'
|
29
|
+
```
|
30
|
+
|
31
|
+
Run the update task :
|
32
|
+
|
33
|
+
```
|
34
|
+
rake radiant:extensions:filter_toolbars:update
|
35
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
# Determine where the RSpec plugin is by loading the boot
|
2
|
+
unless defined? RADIANT_ROOT
|
3
|
+
ENV["RAILS_ENV"] = "test"
|
4
|
+
case
|
5
|
+
when ENV["RADIANT_ENV_FILE"]
|
6
|
+
require File.dirname(ENV["RADIANT_ENV_FILE"]) + "/boot"
|
7
|
+
when File.dirname(__FILE__) =~ %r{vendor/radiant/vendor/extensions}
|
8
|
+
require "#{File.expand_path(File.dirname(__FILE__) + "/../../../../../")}/config/boot"
|
9
|
+
else
|
10
|
+
require "#{File.expand_path(File.dirname(__FILE__) + "/../../../")}/config/boot"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
require 'rake'
|
15
|
+
require 'rake/rdoctask'
|
16
|
+
require 'rake/testtask'
|
17
|
+
|
18
|
+
rspec_base = File.expand_path(RADIANT_ROOT + '/vendor/plugins/rspec/lib')
|
19
|
+
$LOAD_PATH.unshift(rspec_base) if File.exist?(rspec_base)
|
20
|
+
require 'spec/rake/spectask'
|
21
|
+
require 'cucumber'
|
22
|
+
require 'cucumber/rake/task'
|
23
|
+
|
24
|
+
# Cleanup the RADIANT_ROOT constant so specs will load the environment
|
25
|
+
Object.send(:remove_const, :RADIANT_ROOT)
|
26
|
+
|
27
|
+
extension_root = File.expand_path(File.dirname(__FILE__))
|
28
|
+
|
29
|
+
task :default => [:spec, :features]
|
30
|
+
task :stats => "spec:statsetup"
|
31
|
+
|
32
|
+
desc "Run all specs in spec directory"
|
33
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
34
|
+
t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
|
35
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
36
|
+
end
|
37
|
+
|
38
|
+
task :features => 'spec:integration'
|
39
|
+
|
40
|
+
namespace :spec do
|
41
|
+
desc "Run all specs in spec directory with RCov"
|
42
|
+
Spec::Rake::SpecTask.new(:rcov) do |t|
|
43
|
+
t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
|
44
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
45
|
+
t.rcov = true
|
46
|
+
t.rcov_opts = ['--exclude', 'spec', '--rails']
|
47
|
+
end
|
48
|
+
|
49
|
+
desc "Print Specdoc for all specs"
|
50
|
+
Spec::Rake::SpecTask.new(:doc) do |t|
|
51
|
+
t.spec_opts = ["--format", "specdoc", "--dry-run"]
|
52
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
53
|
+
end
|
54
|
+
|
55
|
+
[:models, :controllers, :views, :helpers].each do |sub|
|
56
|
+
desc "Run the specs under spec/#{sub}"
|
57
|
+
Spec::Rake::SpecTask.new(sub) do |t|
|
58
|
+
t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
|
59
|
+
t.spec_files = FileList["spec/#{sub}/**/*_spec.rb"]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
desc "Run the Cucumber features"
|
64
|
+
Cucumber::Rake::Task.new(:integration) do |t|
|
65
|
+
t.fork = true
|
66
|
+
t.cucumber_opts = ['--format', (ENV['CUCUMBER_FORMAT'] || 'pretty')]
|
67
|
+
# t.feature_pattern = "#{extension_root}/features/**/*.feature"
|
68
|
+
t.profile = "default"
|
69
|
+
end
|
70
|
+
|
71
|
+
# Setup specs for stats
|
72
|
+
task :statsetup do
|
73
|
+
require 'code_statistics'
|
74
|
+
::STATS_DIRECTORIES << %w(Model\ specs spec/models)
|
75
|
+
::STATS_DIRECTORIES << %w(View\ specs spec/views)
|
76
|
+
::STATS_DIRECTORIES << %w(Controller\ specs spec/controllers)
|
77
|
+
::STATS_DIRECTORIES << %w(Helper\ specs spec/views)
|
78
|
+
::CodeStatistics::TEST_TYPES << "Model specs"
|
79
|
+
::CodeStatistics::TEST_TYPES << "View specs"
|
80
|
+
::CodeStatistics::TEST_TYPES << "Controller specs"
|
81
|
+
::CodeStatistics::TEST_TYPES << "Helper specs"
|
82
|
+
::STATS_DIRECTORIES.delete_if {|a| a[0] =~ /test/}
|
83
|
+
end
|
84
|
+
|
85
|
+
namespace :db do
|
86
|
+
namespace :fixtures do
|
87
|
+
desc "Load fixtures (from spec/fixtures) into the current environment's database. Load specific fixtures using FIXTURES=x,y"
|
88
|
+
task :load => :environment do
|
89
|
+
require 'active_record/fixtures'
|
90
|
+
ActiveRecord::Base.establish_connection(RAILS_ENV.to_sym)
|
91
|
+
(ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/) : Dir.glob(File.join(RAILS_ROOT, 'spec', 'fixtures', '*.{yml,csv}'))).each do |fixture_file|
|
92
|
+
Fixtures.create_fixtures('spec/fixtures', File.basename(fixture_file, '.*'))
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
desc 'Generate documentation for the filter_toolbars extension.'
|
100
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
101
|
+
rdoc.rdoc_dir = 'rdoc'
|
102
|
+
rdoc.title = 'FilterToolbarsExtension'
|
103
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
104
|
+
rdoc.rdoc_files.include('README')
|
105
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
106
|
+
end
|
107
|
+
|
108
|
+
# Load any custom rakefiles for extension
|
109
|
+
Dir[File.dirname(__FILE__) + '/tasks/*.rake'].sort.each { |f| require f }
|
data/cucumber.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
default: --format progress features --tags ~@proposed,~@in_progress
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# Sets up the Rails environment for Cucumber
|
2
|
+
ENV["RAILS_ENV"] = "test"
|
3
|
+
# Extension root
|
4
|
+
extension_env = File.expand_path(File.dirname(__FILE__) + '/../../../../../config/environment')
|
5
|
+
require extension_env+'.rb'
|
6
|
+
|
7
|
+
Dir.glob(File.join(RADIANT_ROOT, "features", "**", "*.rb")).each {|step| require step unless step =~ /datasets_loader\.rb$/}
|
8
|
+
|
9
|
+
Cucumber::Rails::World.class_eval do
|
10
|
+
dataset :filter_toolbars
|
11
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module NavigationHelpers
|
2
|
+
|
3
|
+
# Extend the standard PathMatchers with your own paths
|
4
|
+
# to be used in your features.
|
5
|
+
#
|
6
|
+
# The keys and values here may be used in your standard web steps
|
7
|
+
# Using:
|
8
|
+
#
|
9
|
+
# When I go to the "filter_toolbars" admin page
|
10
|
+
#
|
11
|
+
# would direct the request to the path you provide in the value:
|
12
|
+
#
|
13
|
+
# admin_filter_toolbars_path
|
14
|
+
#
|
15
|
+
PathMatchers = {} unless defined?(PathMatchers)
|
16
|
+
PathMatchers.merge!({
|
17
|
+
# /filter_toolbars/i => 'admin_filter_toolbars_path'
|
18
|
+
})
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
World(NavigationHelpers)
|
@@ -0,0 +1,9 @@
|
|
1
|
+
class FilterToolbarsExtension < Radiant::Extension
|
2
|
+
version RadiantFilterToolbarsExtension::VERSION
|
3
|
+
description RadiantFilterToolbarsExtension::DESCRIPTION
|
4
|
+
url RadiantFilterToolbarsExtension::HOMEPAGE
|
5
|
+
|
6
|
+
def activate
|
7
|
+
Admin::PagesController.send :include, RadiantFilterToolbarsExtension::Controllers::Admin::PagesController
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module RadiantFilterToolbarsExtension
|
2
|
+
module Controllers
|
3
|
+
module Admin
|
4
|
+
module PagesController
|
5
|
+
def self.included(base)
|
6
|
+
base.class_eval do
|
7
|
+
before_filter :filter_toolbar_assets, :only => [:new, :edit]
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def filter_toolbar_assets
|
12
|
+
@javascripts << 'admin/filter_toolbars/control/livepipe'
|
13
|
+
@javascripts << 'admin/filter_toolbars/control/textarea'
|
14
|
+
@javascripts << 'admin/filter_toolbars/filter_toolbars'
|
15
|
+
@javascripts << 'admin/filter_toolbars/filters/textile'
|
16
|
+
@javascripts << 'admin/filter_toolbars/filters/markdown'
|
17
|
+
@stylesheets << 'admin/filter_toolbars'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
namespace :radiant do
|
2
|
+
namespace :extensions do
|
3
|
+
namespace :filter_toolbars do
|
4
|
+
|
5
|
+
desc "Runs the migration of the Filter Toolbars extension"
|
6
|
+
task :migrate => :environment do
|
7
|
+
require 'radiant/extension_migrator'
|
8
|
+
if ENV["VERSION"]
|
9
|
+
FilterToolbarsExtension.migrator.migrate(ENV["VERSION"].to_i)
|
10
|
+
Rake::Task['db:schema:dump'].invoke
|
11
|
+
else
|
12
|
+
FilterToolbarsExtension.migrator.migrate
|
13
|
+
Rake::Task['db:schema:dump'].invoke
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "Copies public assets of the Filter Toolbars to the instance public/ directory."
|
18
|
+
task :update => :environment do
|
19
|
+
is_svn_or_dir = proc {|path| path =~ /\.svn/ || File.directory?(path) }
|
20
|
+
puts "Copying assets from FilterToolbarsExtension"
|
21
|
+
Dir[FilterToolbarsExtension.root + "/public/**/*"].reject(&is_svn_or_dir).each do |file|
|
22
|
+
path = file.sub(FilterToolbarsExtension.root, '')
|
23
|
+
directory = File.dirname(path)
|
24
|
+
mkdir_p RAILS_ROOT + directory, :verbose => false
|
25
|
+
cp file, RAILS_ROOT + path, :verbose => false
|
26
|
+
end
|
27
|
+
unless FilterToolbarsExtension.root.starts_with? RAILS_ROOT # don't need to copy vendored tasks
|
28
|
+
puts "Copying rake tasks from FilterToolbarsExtension"
|
29
|
+
local_tasks_path = File.join(RAILS_ROOT, %w(lib tasks))
|
30
|
+
mkdir_p local_tasks_path, :verbose => false
|
31
|
+
Dir[File.join FilterToolbarsExtension.root, %w(lib tasks *.rake)].each do |file|
|
32
|
+
cp file, local_tasks_path, :verbose => false
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
desc "Syncs all available translations for this ext to the English ext master"
|
38
|
+
task :sync => :environment do
|
39
|
+
# The main translation root, basically where English is kept
|
40
|
+
language_root = FilterToolbarsExtension.root + "/config/locales"
|
41
|
+
words = TranslationSupport.get_translation_keys(language_root)
|
42
|
+
|
43
|
+
Dir["#{language_root}/*.yml"].each do |filename|
|
44
|
+
next if filename.match('_available_tags')
|
45
|
+
basename = File.basename(filename, '.yml')
|
46
|
+
puts "Syncing #{basename}"
|
47
|
+
(comments, other) = TranslationSupport.read_file(filename, basename)
|
48
|
+
words.each { |k,v| other[k] ||= words[k] } # Initializing hash variable as empty if it does not exist
|
49
|
+
other.delete_if { |k,v| !words[k] } # Remove if not defined in en.yml
|
50
|
+
TranslationSupport.write_file(filename, basename, comments, other)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
Binary file
|
@@ -0,0 +1,181 @@
|
|
1
|
+
/**
|
2
|
+
* @author Ryan Johnson <http://syntacticx.com/>
|
3
|
+
* @copyright 2008 PersonalGrid Corporation <http://personalgrid.com/>
|
4
|
+
* @package LivePipe UI
|
5
|
+
* @license MIT
|
6
|
+
* @url http://livepipe.net/core
|
7
|
+
* @require prototype.js
|
8
|
+
*/
|
9
|
+
|
10
|
+
if(typeof(Control) == 'undefined')
|
11
|
+
Control = {};
|
12
|
+
|
13
|
+
var $proc = function(proc){
|
14
|
+
return typeof(proc) == 'function' ? proc : function(){return proc};
|
15
|
+
};
|
16
|
+
|
17
|
+
var $value = function(value){
|
18
|
+
return typeof(value) == 'function' ? value() : value;
|
19
|
+
};
|
20
|
+
|
21
|
+
Object.Event = {
|
22
|
+
extend: function(object){
|
23
|
+
object._objectEventSetup = function(event_name){
|
24
|
+
this._observers = this._observers || {};
|
25
|
+
this._observers[event_name] = this._observers[event_name] || [];
|
26
|
+
};
|
27
|
+
object.observe = function(event_name,observer){
|
28
|
+
if(typeof(event_name) == 'string' && typeof(observer) != 'undefined'){
|
29
|
+
this._objectEventSetup(event_name);
|
30
|
+
if(!this._observers[event_name].include(observer))
|
31
|
+
this._observers[event_name].push(observer);
|
32
|
+
}else
|
33
|
+
for(var e in event_name)
|
34
|
+
this.observe(e,event_name[e]);
|
35
|
+
};
|
36
|
+
object.stopObserving = function(event_name,observer){
|
37
|
+
this._objectEventSetup(event_name);
|
38
|
+
if(event_name && observer)
|
39
|
+
this._observers[event_name] = this._observers[event_name].without(observer);
|
40
|
+
else if(event_name)
|
41
|
+
this._observers[event_name] = [];
|
42
|
+
else
|
43
|
+
this._observers = {};
|
44
|
+
};
|
45
|
+
object.observeOnce = function(event_name,outer_observer){
|
46
|
+
var inner_observer = function(){
|
47
|
+
outer_observer.apply(this,arguments);
|
48
|
+
this.stopObserving(event_name,inner_observer);
|
49
|
+
}.bind(this);
|
50
|
+
this._objectEventSetup(event_name);
|
51
|
+
this._observers[event_name].push(inner_observer);
|
52
|
+
};
|
53
|
+
object.notify = function(event_name){
|
54
|
+
this._objectEventSetup(event_name);
|
55
|
+
var collected_return_values = [];
|
56
|
+
var args = $A(arguments).slice(1);
|
57
|
+
try{
|
58
|
+
for(var i = 0; i < this._observers[event_name].length; ++i)
|
59
|
+
collected_return_values.push(this._observers[event_name][i].apply(this,args) || null);
|
60
|
+
}catch(e){
|
61
|
+
if(e == $break)
|
62
|
+
return false;
|
63
|
+
else
|
64
|
+
throw e;
|
65
|
+
}
|
66
|
+
return collected_return_values;
|
67
|
+
};
|
68
|
+
if(object.prototype){
|
69
|
+
object.prototype._objectEventSetup = object._objectEventSetup;
|
70
|
+
object.prototype.observe = object.observe;
|
71
|
+
object.prototype.stopObserving = object.stopObserving;
|
72
|
+
object.prototype.observeOnce = object.observeOnce;
|
73
|
+
object.prototype.notify = function(event_name){
|
74
|
+
if(object.notify){
|
75
|
+
var args = $A(arguments).slice(1);
|
76
|
+
args.unshift(this);
|
77
|
+
args.unshift(event_name);
|
78
|
+
object.notify.apply(object,args);
|
79
|
+
}
|
80
|
+
this._objectEventSetup(event_name);
|
81
|
+
var args = $A(arguments).slice(1);
|
82
|
+
var collected_return_values = [];
|
83
|
+
try{
|
84
|
+
if(this.options && this.options[event_name] && typeof(this.options[event_name]) == 'function')
|
85
|
+
collected_return_values.push(this.options[event_name].apply(this,args) || null);
|
86
|
+
var callbacks_copy = this._observers[event_name]; // since original array will be modified after observeOnce calls
|
87
|
+
for(var i = 0; i < callbacks_copy.length; ++i)
|
88
|
+
collected_return_values.push(callbacks_copy[i].apply(this,args) || null);
|
89
|
+
}catch(e){
|
90
|
+
if(e == $break)
|
91
|
+
return false;
|
92
|
+
else
|
93
|
+
throw e;
|
94
|
+
}
|
95
|
+
return collected_return_values;
|
96
|
+
};
|
97
|
+
}
|
98
|
+
}
|
99
|
+
};
|
100
|
+
|
101
|
+
/* Begin Core Extensions */
|
102
|
+
|
103
|
+
//Element.observeOnce
|
104
|
+
Element.addMethods({
|
105
|
+
observeOnce: function(element,event_name,outer_callback){
|
106
|
+
var inner_callback = function(){
|
107
|
+
outer_callback.apply(this,arguments);
|
108
|
+
Element.stopObserving(element,event_name,inner_callback);
|
109
|
+
};
|
110
|
+
Element.observe(element,event_name,inner_callback);
|
111
|
+
}
|
112
|
+
});
|
113
|
+
|
114
|
+
//mouse:wheel
|
115
|
+
(function(){
|
116
|
+
function wheel(event){
|
117
|
+
var delta, element, custom_event;
|
118
|
+
// normalize the delta
|
119
|
+
if (event.wheelDelta) { // IE & Opera
|
120
|
+
delta = event.wheelDelta / 120;
|
121
|
+
} else if (event.detail) { // W3C
|
122
|
+
delta =- event.detail / 3;
|
123
|
+
}
|
124
|
+
if (!delta) { return; }
|
125
|
+
element = Event.extend(event).target;
|
126
|
+
element = Element.extend(element.nodeType === Node.TEXT_NODE ? element.parentNode : element);
|
127
|
+
custom_event = element.fire('mouse:wheel',{ delta: delta });
|
128
|
+
if (custom_event.stopped) {
|
129
|
+
Event.stop(event);
|
130
|
+
return false;
|
131
|
+
}
|
132
|
+
}
|
133
|
+
document.observe('mousewheel',wheel);
|
134
|
+
document.observe('DOMMouseScroll',wheel);
|
135
|
+
})();
|
136
|
+
|
137
|
+
/* End Core Extensions */
|
138
|
+
|
139
|
+
//from PrototypeUI
|
140
|
+
var IframeShim = Class.create({
|
141
|
+
initialize: function() {
|
142
|
+
this.element = new Element('iframe',{
|
143
|
+
style: 'position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);display:none',
|
144
|
+
src: 'javascript:void(0);',
|
145
|
+
frameborder: 0
|
146
|
+
});
|
147
|
+
$(document.body).insert(this.element);
|
148
|
+
},
|
149
|
+
hide: function() {
|
150
|
+
this.element.hide();
|
151
|
+
return this;
|
152
|
+
},
|
153
|
+
show: function() {
|
154
|
+
this.element.show();
|
155
|
+
return this;
|
156
|
+
},
|
157
|
+
positionUnder: function(element) {
|
158
|
+
var element = $(element);
|
159
|
+
var offset = element.cumulativeOffset();
|
160
|
+
var dimensions = element.getDimensions();
|
161
|
+
this.element.setStyle({
|
162
|
+
left: offset[0] + 'px',
|
163
|
+
top: offset[1] + 'px',
|
164
|
+
width: dimensions.width + 'px',
|
165
|
+
height: dimensions.height + 'px',
|
166
|
+
zIndex: element.getStyle('zIndex') - 1
|
167
|
+
}).show();
|
168
|
+
return this;
|
169
|
+
},
|
170
|
+
setBounds: function(bounds) {
|
171
|
+
for(prop in bounds)
|
172
|
+
bounds[prop] += 'px';
|
173
|
+
this.element.setStyle(bounds);
|
174
|
+
return this;
|
175
|
+
},
|
176
|
+
destroy: function() {
|
177
|
+
if(this.element)
|
178
|
+
this.element.remove();
|
179
|
+
return this;
|
180
|
+
}
|
181
|
+
});
|
@@ -0,0 +1,123 @@
|
|
1
|
+
/**
|
2
|
+
* @author Ryan Johnson <http://syntacticx.com/>
|
3
|
+
* @copyright 2008 PersonalGrid Corporation <http://personalgrid.com/>
|
4
|
+
* @package LivePipe UI
|
5
|
+
* @license MIT
|
6
|
+
* @url http://livepipe.net/control/textarea
|
7
|
+
* @require prototype.js, livepipe.js
|
8
|
+
*/
|
9
|
+
|
10
|
+
/*global window, document, Prototype, Class, $, $A, Control */
|
11
|
+
|
12
|
+
if(typeof(Prototype) == "undefined") {
|
13
|
+
throw "Control.TextArea requires Prototype to be loaded."; }
|
14
|
+
if(typeof(Object.Event) == "undefined") {
|
15
|
+
throw "Control.TextArea requires Object.Event to be loaded."; }
|
16
|
+
|
17
|
+
Control.TextArea = Class.create({
|
18
|
+
initialize: function(textarea){
|
19
|
+
this.onChangeTimeout = false;
|
20
|
+
this.element = $(textarea);
|
21
|
+
$(this.element).observe('keyup',this.doOnChange.bindAsEventListener(this));
|
22
|
+
$(this.element).observe('paste',this.doOnChange.bindAsEventListener(this));
|
23
|
+
$(this.element).observe('input',this.doOnChange.bindAsEventListener(this));
|
24
|
+
if(!!document.selection){
|
25
|
+
$(this.element).observe('mouseup',this.saveRange.bindAsEventListener(this));
|
26
|
+
$(this.element).observe('keyup',this.saveRange.bindAsEventListener(this));
|
27
|
+
}
|
28
|
+
},
|
29
|
+
doOnChange: function(event){
|
30
|
+
if(this.onChangeTimeout) {
|
31
|
+
window.clearTimeout(this.onChangeTimeout); }
|
32
|
+
this.onChangeTimeout = window.setTimeout(function(){
|
33
|
+
this.notify('change',this.getValue());
|
34
|
+
}.bind(this),Control.TextArea.onChangeTimeoutLength);
|
35
|
+
},
|
36
|
+
saveRange: function(){
|
37
|
+
this.range = document.selection.createRange();
|
38
|
+
},
|
39
|
+
getValue: function(){
|
40
|
+
return this.element.value;
|
41
|
+
},
|
42
|
+
getSelection: function(){
|
43
|
+
if(!!document.selection) {
|
44
|
+
return document.selection.createRange().text; }
|
45
|
+
else if(!!this.element.setSelectionRange) {
|
46
|
+
return this.element.value.substring(this.element.selectionStart,this.element.selectionEnd); }
|
47
|
+
else {
|
48
|
+
return false; }
|
49
|
+
},
|
50
|
+
replaceSelection: function(text){
|
51
|
+
var scroll_top = this.element.scrollTop;
|
52
|
+
if(!!document.selection){
|
53
|
+
this.element.focus();
|
54
|
+
var range = (this.range) ? this.range : document.selection.createRange();
|
55
|
+
range.text = text;
|
56
|
+
range.select();
|
57
|
+
}else if(!!this.element.setSelectionRange){
|
58
|
+
var selection_start = this.element.selectionStart;
|
59
|
+
this.element.value = this.element.value.substring(0,selection_start) + text + this.element.value.substring(this.element.selectionEnd);
|
60
|
+
this.element.setSelectionRange(selection_start + text.length,selection_start + text.length);
|
61
|
+
}
|
62
|
+
this.doOnChange();
|
63
|
+
this.element.focus();
|
64
|
+
this.element.scrollTop = scroll_top;
|
65
|
+
},
|
66
|
+
wrapSelection: function(before,after){
|
67
|
+
var sel = this.getSelection();
|
68
|
+
// Remove the wrapping if the selection has the same before/after
|
69
|
+
if (sel.indexOf(before) === 0 &&
|
70
|
+
sel.lastIndexOf(after) === (sel.length - after.length)) {
|
71
|
+
this.replaceSelection(sel.substring(before.length,
|
72
|
+
sel.length - after.length));
|
73
|
+
} else { this.replaceSelection(before + sel + after); }
|
74
|
+
},
|
75
|
+
insertBeforeSelection: function(text){
|
76
|
+
this.replaceSelection(text + this.getSelection());
|
77
|
+
},
|
78
|
+
insertAfterSelection: function(text){
|
79
|
+
this.replaceSelection(this.getSelection() + text);
|
80
|
+
},
|
81
|
+
collectFromEachSelectedLine: function(callback,before,after){
|
82
|
+
this.replaceSelection((before || '') + $A(this.getSelection().split("\n")).collect(callback).join("\n") + (after || ''));
|
83
|
+
},
|
84
|
+
insertBeforeEachSelectedLine: function(text,before,after){
|
85
|
+
this.collectFromEachSelectedLine(function(line){
|
86
|
+
},before,after);
|
87
|
+
}
|
88
|
+
});
|
89
|
+
Object.extend(Control.TextArea,{
|
90
|
+
onChangeTimeoutLength: 500
|
91
|
+
});
|
92
|
+
Object.Event.extend(Control.TextArea);
|
93
|
+
|
94
|
+
Control.TextArea.ToolBar = Class.create( {
|
95
|
+
initialize: function(textarea,toolbar){
|
96
|
+
this.textarea = textarea;
|
97
|
+
if(toolbar) {
|
98
|
+
this.container = $(toolbar); }
|
99
|
+
else{
|
100
|
+
this.container = $(document.createElement('ul'));
|
101
|
+
this.textarea.element.parentNode.insertBefore(this.container,this.textarea.element);
|
102
|
+
}
|
103
|
+
},
|
104
|
+
attachButton: function(node,callback){
|
105
|
+
node.onclick = function(){return false;};
|
106
|
+
$(node).observe('click',callback.bindAsEventListener(this.textarea));
|
107
|
+
},
|
108
|
+
addButton: function(link_text,callback,attrs){
|
109
|
+
var li = document.createElement('li');
|
110
|
+
var a = document.createElement('a');
|
111
|
+
a.href = '#';
|
112
|
+
this.attachButton(a,callback);
|
113
|
+
li.appendChild(a);
|
114
|
+
Object.extend(a,attrs || {});
|
115
|
+
if(link_text){
|
116
|
+
var span = document.createElement('span');
|
117
|
+
span.innerHTML = link_text;
|
118
|
+
a.appendChild(span);
|
119
|
+
}
|
120
|
+
this.container.appendChild(li);
|
121
|
+
}
|
122
|
+
});
|
123
|
+
|
@@ -0,0 +1,81 @@
|
|
1
|
+
var FilterToolBars = {
|
2
|
+
className: 'filter_toolbar',
|
3
|
+
textarea: null,
|
4
|
+
buttons: [
|
5
|
+
{ title: 'Bold', slug: 'bold' },
|
6
|
+
{ title: 'Italics', slug: 'italics' },
|
7
|
+
{ title: 'Heading 1', slug: 'h1' },
|
8
|
+
{ title: 'Heading 2', slug: 'h2' },
|
9
|
+
{ title: 'Heading 3', slug: 'h3' },
|
10
|
+
{ title: 'Heading 4', slug: 'h4' },
|
11
|
+
{ title: 'Ordered List', slug: 'ordered' },
|
12
|
+
{ title: 'Unordered List', slug: 'unordered' },
|
13
|
+
{ title: 'Link', slug: 'link' },
|
14
|
+
{ title: 'Image', slug: 'image', attrs: {href:'#attach_asset'}},
|
15
|
+
{ title: 'Block Quote', slug: 'quote' },
|
16
|
+
{ title: 'Help', slug: 'help' }
|
17
|
+
]
|
18
|
+
}
|
19
|
+
|
20
|
+
FilterToolBars.Filters = {
|
21
|
+
image: function() {
|
22
|
+
return false;
|
23
|
+
},
|
24
|
+
|
25
|
+
help: function() {
|
26
|
+
page_part = FilterToolBars.textarea.element.up('.page').readAttribute('data-caption');
|
27
|
+
loadFilterReference(page_part);
|
28
|
+
}
|
29
|
+
}
|
30
|
+
|
31
|
+
FilterToolBars.AttachBehavior = Behavior.create({
|
32
|
+
initialize: function(options) {
|
33
|
+
this.textarea = new Control.TextArea(this.element);
|
34
|
+
this.toolbar = new Control.TextArea.ToolBar(this.textarea);
|
35
|
+
this.toolbar.container.id = this.element.id + '_toolbar';
|
36
|
+
this.toolbar.container.className = FilterToolBars.className;
|
37
|
+
this.attach();
|
38
|
+
},
|
39
|
+
|
40
|
+
attach: function() {
|
41
|
+
FilterToolBars.buttons.each( function(button) {
|
42
|
+
var attributes = Object.extend({ className: 'button_' + button.slug, title: button.title }, button.attrs || {})
|
43
|
+
this.toolbar.addButton(button.title, function() { this.click(button.slug) }.bind(this), attributes);
|
44
|
+
}.bind(this));
|
45
|
+
},
|
46
|
+
|
47
|
+
click: function(tag) {
|
48
|
+
FilterToolBars.textarea = this.textarea;
|
49
|
+
FilterToolBars[this.filter()][tag]();
|
50
|
+
},
|
51
|
+
|
52
|
+
filter: function() {
|
53
|
+
return this.element.up('.part').down('select').getValue();
|
54
|
+
},
|
55
|
+
|
56
|
+
show: function() {
|
57
|
+
this.toolbar.show();
|
58
|
+
},
|
59
|
+
|
60
|
+
hide: function() {
|
61
|
+
this.toolbar.hide();
|
62
|
+
}
|
63
|
+
});
|
64
|
+
|
65
|
+
FilterToolBars.ChangeBehavior = Behavior.create({
|
66
|
+
initialize: function(options) {
|
67
|
+
this.change();
|
68
|
+
this.element.observe('change', this.change.bind(this));
|
69
|
+
},
|
70
|
+
|
71
|
+
change: function() {
|
72
|
+
FilterToolBars.filter = this.element.getValue()
|
73
|
+
}
|
74
|
+
});
|
75
|
+
|
76
|
+
Event.addBehavior({
|
77
|
+
'.part select' : FilterToolBars.ChangeBehavior(),
|
78
|
+
'.part .textarea' : FilterToolBars.AttachBehavior(),
|
79
|
+
'a.button_image' : Popup.TriggerBehavior()
|
80
|
+
});
|
81
|
+
|
@@ -0,0 +1,57 @@
|
|
1
|
+
FilterToolBars.Markdown = Object.extend({
|
2
|
+
bold: function() {
|
3
|
+
FilterToolBars.textarea.wrapSelection('**', '**');
|
4
|
+
},
|
5
|
+
|
6
|
+
italics: function() {
|
7
|
+
FilterToolBars.textarea.wrapSelection('*', '*');
|
8
|
+
},
|
9
|
+
|
10
|
+
h1: function() {
|
11
|
+
FilterToolBars.textarea.insertBeforeSelection('# ');
|
12
|
+
},
|
13
|
+
|
14
|
+
h2: function() {
|
15
|
+
FilterToolBars.textarea.insertBeforeSelection('## ');
|
16
|
+
},
|
17
|
+
|
18
|
+
h3: function() {
|
19
|
+
FilterToolBars.textarea.insertBeforeSelection('### ');
|
20
|
+
},
|
21
|
+
|
22
|
+
h4: function() {
|
23
|
+
FilterToolBars.textarea.insertBeforeSelection('#### ');
|
24
|
+
},
|
25
|
+
|
26
|
+
ordered: function() {
|
27
|
+
var i = 0;
|
28
|
+
|
29
|
+
FilterToolBars.textarea.collectFromEachSelectedLine( function(line) {
|
30
|
+
if (!line.match(/^\s+$/)) {
|
31
|
+
++i;
|
32
|
+
return event.shiftKey ? line.replace(/^\d+\.\s/,'') : (line.match(/\d+\.\s/) ? '' : i + '. ') + line;
|
33
|
+
}
|
34
|
+
});
|
35
|
+
},
|
36
|
+
|
37
|
+
unordered: function() {
|
38
|
+
FilterToolBars.textarea.collectFromEachSelectedLine( function(line) {
|
39
|
+
return event.shiftKey ? (line.match(/^\*{2,}/) ? line.replace(/^\*/,'') : line.replace(/^\*\s/,'')) : (line.match(/\*+\s/) ? '*' : '* ') + line;
|
40
|
+
});
|
41
|
+
},
|
42
|
+
|
43
|
+
link: function() {
|
44
|
+
var selection = this.getSelection() || 'Link Text',
|
45
|
+
response = prompt('Enter Full URL: (http:// or mailto:)', '');
|
46
|
+
|
47
|
+
if (response && response.match(/^((f|ht)tps?|mailto)/)) {
|
48
|
+
FilterToolBars.textarea.replaceSelection('[' + selection + '](' + response.replace(/^(?!(f|ht)tps?:\/\/)/, 'http://') + ')');
|
49
|
+
}
|
50
|
+
},
|
51
|
+
|
52
|
+
quote: function() {
|
53
|
+
FilterToolBars.textarea.collectFromEachSelectedLine( function(line) {
|
54
|
+
return event.shiftKey ? line.replace(/^\> /,'') : '> ' + line;
|
55
|
+
});
|
56
|
+
}
|
57
|
+
}, FilterToolBars.Filters);
|
@@ -0,0 +1,52 @@
|
|
1
|
+
FilterToolBars.Textile = Object.extend({
|
2
|
+
bold: function() {
|
3
|
+
FilterToolBars.textarea.wrapSelection('*', '*');
|
4
|
+
},
|
5
|
+
|
6
|
+
italics: function() {
|
7
|
+
FilterToolBars.textarea.wrapSelection('_', '_');
|
8
|
+
},
|
9
|
+
|
10
|
+
h1: function() {
|
11
|
+
FilterToolBars.textarea.insertBeforeSelection('h1. ');
|
12
|
+
},
|
13
|
+
|
14
|
+
h2: function() {
|
15
|
+
FilterToolBars.textarea.insertBeforeSelection('h2. ');
|
16
|
+
},
|
17
|
+
|
18
|
+
h3: function() {
|
19
|
+
FilterToolBars.textarea.insertBeforeSelection('h3. ');
|
20
|
+
},
|
21
|
+
|
22
|
+
h4: function() {
|
23
|
+
FilterToolBars.textarea.insertBeforeSelection('h4. ');
|
24
|
+
},
|
25
|
+
|
26
|
+
ordered: function() {
|
27
|
+
FilterToolBars.textarea.collectFromEachSelectedLine( function(line) {
|
28
|
+
return event.shiftKey ? (line.match(/^\#{2,}/) ? line.replace(/^\#/,'') : line.replace(/^\#\s/,'')) : (line.match(/\#+\s/) ? '#' : '# ') + line;
|
29
|
+
});
|
30
|
+
},
|
31
|
+
|
32
|
+
unordered: function() {
|
33
|
+
FilterToolBars.textarea.collectFromEachSelectedLine( function(line) {
|
34
|
+
return event.shiftKey ? (line.match(/^\*{2,}/) ? line.replace(/^\*/,'') : line.replace(/^\*\s/,'')) : (line.match(/\*+\s/) ? '*' : '* ') + line;
|
35
|
+
});
|
36
|
+
},
|
37
|
+
|
38
|
+
link: function() {
|
39
|
+
var selection = FilterToolBars.textarea.getSelection() || 'Link Text',
|
40
|
+
response = prompt('Enter Full URL: (http:// or mailto:)', '');
|
41
|
+
|
42
|
+
if (response && response.match(/^((f|ht)tps?|mailto)/)) {
|
43
|
+
FilterToolBars.textarea.replaceSelection('"' + selection + '":' + response.replace(/^(?!(f|ht)tps?:\/\/)/, 'http://'));
|
44
|
+
}
|
45
|
+
},
|
46
|
+
|
47
|
+
quote: function() {
|
48
|
+
FilterToolBars.textarea.collectFromEachSelectedLine( function(line) {
|
49
|
+
return event.shiftKey ? line.replace(/^bq. /,'') : 'bq. ' + line;
|
50
|
+
});
|
51
|
+
}
|
52
|
+
}, FilterToolBars.Filters);
|
@@ -0,0 +1,62 @@
|
|
1
|
+
.textarea {
|
2
|
+
padding:2px;
|
3
|
+
}
|
4
|
+
|
5
|
+
.filter_toolbar {
|
6
|
+
position:relative;
|
7
|
+
margin:0;
|
8
|
+
padding:0 2px;
|
9
|
+
border:1px solid #ccc;
|
10
|
+
background-color:#F6F6F6;
|
11
|
+
width:100%;
|
12
|
+
height:26px;
|
13
|
+
border-bottom: 0;
|
14
|
+
|
15
|
+
li {
|
16
|
+
list-style:none;
|
17
|
+
float:left;
|
18
|
+
|
19
|
+
span {
|
20
|
+
display:none;
|
21
|
+
}
|
22
|
+
|
23
|
+
a {
|
24
|
+
margin: 2px;
|
25
|
+
width:20px;
|
26
|
+
height:20px;
|
27
|
+
float:left;
|
28
|
+
display:block;
|
29
|
+
background-image:url('/images/admin/filter_toolbars/icons.gif');
|
30
|
+
border:1px solid #f6f6f6;
|
31
|
+
|
32
|
+
&:hover {
|
33
|
+
border-color:#0A246A;
|
34
|
+
background-color: #B2BBD0;
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
.button_help {
|
39
|
+
position:absolute;
|
40
|
+
top:0;
|
41
|
+
right:0;
|
42
|
+
|
43
|
+
&:hover {
|
44
|
+
border-left-color:#5f9846;
|
45
|
+
border-right-color:#5f9846;
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
.button_bold { background-position:0 0 }
|
50
|
+
.button_italics { background-position:-60px 0 }
|
51
|
+
.button_h1 { background-position: -660px 0 }
|
52
|
+
.button_h2 { background-position: -680px 0 }
|
53
|
+
.button_h3 { background-position: -700px 0 }
|
54
|
+
.button_h4 { background-position: -720px 0 }
|
55
|
+
.button_ordered { background-position:-80px 0 }
|
56
|
+
.button_unordered { background-position:-20px 0 }
|
57
|
+
.button_link { background-position:-500px 0 }
|
58
|
+
.button_image { background-position:-380px 0 }
|
59
|
+
.button_quote { background-position:-220px 0 }
|
60
|
+
.button_help { background-position:-340px 0 }
|
61
|
+
}
|
62
|
+
}
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "radiant_filter_toolbars_extension"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.platform = Gem::Platform::RUBY
|
7
|
+
s.name = "radiant-filter_toolbars-extension"
|
8
|
+
s.version = RadiantFilterToolbarsExtension::VERSION
|
9
|
+
s.summary = RadiantFilterToolbarsExtension::SUMMARY
|
10
|
+
s.description = RadiantFilterToolbarsExtension::DESCRIPTION
|
11
|
+
s.homepage = RadiantFilterToolbarsExtension::HOMEPAGE
|
12
|
+
s.authors = ["Jason Taylor"]
|
13
|
+
s.email = ["jsntv200@gmail.com"]
|
14
|
+
|
15
|
+
ignores = if File.exist?('.gitignore')
|
16
|
+
File.read('.gitignore').split("\n").inject([]) {|a,p| a + Dir[p] }
|
17
|
+
else
|
18
|
+
[]
|
19
|
+
end
|
20
|
+
|
21
|
+
s.files = Dir['**/*'] - ignores
|
22
|
+
s.test_files = Dir['test/**/*','spec/**/*','features/**/*'] - ignores
|
23
|
+
s.require_paths = ["lib"]
|
24
|
+
|
25
|
+
s.post_install_message = %{
|
26
|
+
Add this to your radiant project with:
|
27
|
+
config.gem 'radiant-filter_toolbars-extension', :version => '~>#{RadiantFilterToolbarsExtension::VERSION}'
|
28
|
+
}
|
29
|
+
end
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
unless defined? RADIANT_ROOT
|
2
|
+
ENV["RAILS_ENV"] = "test"
|
3
|
+
case
|
4
|
+
when ENV["RADIANT_ENV_FILE"]
|
5
|
+
require ENV["RADIANT_ENV_FILE"]
|
6
|
+
when File.dirname(__FILE__) =~ %r{vendor/radiant/vendor/extensions}
|
7
|
+
require "#{File.expand_path(File.dirname(__FILE__) + "/../../../../../../")}/config/environment"
|
8
|
+
else
|
9
|
+
require "#{File.expand_path(File.dirname(__FILE__) + "/../../../../")}/config/environment"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
require "#{RADIANT_ROOT}/spec/spec_helper"
|
13
|
+
|
14
|
+
Dataset::Resolver.default << (File.dirname(__FILE__) + "/datasets")
|
15
|
+
|
16
|
+
if File.directory?(File.dirname(__FILE__) + "/matchers")
|
17
|
+
Dir[File.dirname(__FILE__) + "/matchers/*.rb"].each {|file| require file }
|
18
|
+
end
|
19
|
+
|
20
|
+
Spec::Runner.configure do |config|
|
21
|
+
# config.use_transactional_fixtures = true
|
22
|
+
# config.use_instantiated_fixtures = false
|
23
|
+
# config.fixture_path = RAILS_ROOT + '/spec/fixtures'
|
24
|
+
|
25
|
+
# You can declare fixtures for each behaviour like this:
|
26
|
+
# describe "...." do
|
27
|
+
# fixtures :table_a, :table_b
|
28
|
+
#
|
29
|
+
# Alternatively, if you prefer to declare them only once, you can
|
30
|
+
# do so here, like so ...
|
31
|
+
#
|
32
|
+
# config.global_fixtures = :table_a, :table_b
|
33
|
+
#
|
34
|
+
# If you declare global fixtures, be aware that they will be declared
|
35
|
+
# for all of your examples, even those that don't use them.
|
36
|
+
end
|
metadata
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: radiant-filter_toolbars-extension
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 11
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 5
|
9
|
+
- 0
|
10
|
+
version: 0.5.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Jason Taylor
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-06-10 00:00:00 +10:00
|
19
|
+
default_executable:
|
20
|
+
dependencies: []
|
21
|
+
|
22
|
+
description: Adds a WYSIWYG toolbar for the textile and markdown filters.
|
23
|
+
email:
|
24
|
+
- jsntv200@gmail.com
|
25
|
+
executables: []
|
26
|
+
|
27
|
+
extensions: []
|
28
|
+
|
29
|
+
extra_rdoc_files: []
|
30
|
+
|
31
|
+
files:
|
32
|
+
- cucumber.yml
|
33
|
+
- features/support/env.rb
|
34
|
+
- features/support/paths.rb
|
35
|
+
- filter_toolbars_extension.rb
|
36
|
+
- lib/radiant_filter_toolbars_extension/controllers/admin/pages_controller.rb
|
37
|
+
- lib/radiant_filter_toolbars_extension.rb
|
38
|
+
- lib/tasks/filter_toolbars_extension_tasks.rake
|
39
|
+
- public/images/admin/filter_toolbars/icons.gif
|
40
|
+
- public/javascripts/admin/filter_toolbars/control/livepipe.js
|
41
|
+
- public/javascripts/admin/filter_toolbars/control/textarea.js
|
42
|
+
- public/javascripts/admin/filter_toolbars/filter_toolbars.js
|
43
|
+
- public/javascripts/admin/filter_toolbars/filters/markdown.js
|
44
|
+
- public/javascripts/admin/filter_toolbars/filters/textile.js
|
45
|
+
- public/stylesheets/sass/admin/filter_toolbars.scss
|
46
|
+
- radiant-filter_toolbars-extension.gemspec
|
47
|
+
- Rakefile
|
48
|
+
- README.md
|
49
|
+
- spec/spec.opts
|
50
|
+
- spec/spec_helper.rb
|
51
|
+
has_rdoc: true
|
52
|
+
homepage: https://github.com/jsntv200/radiant-filter_toolbars-extension
|
53
|
+
licenses: []
|
54
|
+
|
55
|
+
post_install_message: "\n Add this to your radiant project with:\n config.gem 'radiant-filter_toolbars-extension', :version => '~>0.5.0'\n "
|
56
|
+
rdoc_options: []
|
57
|
+
|
58
|
+
require_paths:
|
59
|
+
- lib
|
60
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ">="
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
hash: 3
|
66
|
+
segments:
|
67
|
+
- 0
|
68
|
+
version: "0"
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
|
+
none: false
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
hash: 3
|
75
|
+
segments:
|
76
|
+
- 0
|
77
|
+
version: "0"
|
78
|
+
requirements: []
|
79
|
+
|
80
|
+
rubyforge_project:
|
81
|
+
rubygems_version: 1.5.0
|
82
|
+
signing_key:
|
83
|
+
specification_version: 3
|
84
|
+
summary: WYSIWYG Toolbar for Radiant CMS
|
85
|
+
test_files:
|
86
|
+
- spec/spec.opts
|
87
|
+
- spec/spec_helper.rb
|
88
|
+
- features/support/env.rb
|
89
|
+
- features/support/paths.rb
|