radiant-filter_toolbars-extension 0.5.0
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.
- 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
|