tiny_mce_plugin_imageselector 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. data/LICENSE +22 -0
  2. data/README.rdoc +24 -0
  3. data/lib/assets/plugins/imageselector/LICENSE +22 -0
  4. data/lib/assets/plugins/imageselector/README.md +29 -0
  5. data/lib/assets/plugins/imageselector/css/media_selector.css +80 -0
  6. data/lib/assets/plugins/imageselector/dialog.htm +48 -0
  7. data/lib/assets/plugins/imageselector/editor_plugin.js +1 -0
  8. data/lib/assets/plugins/imageselector/editor_plugin_src.js +81 -0
  9. data/lib/assets/plugins/imageselector/img/example.gif +0 -0
  10. data/lib/assets/plugins/imageselector/img/imageselector.png +0 -0
  11. data/lib/assets/plugins/imageselector/img/spinner.gif +0 -0
  12. data/lib/assets/plugins/imageselector/js/media_selector.js +562 -0
  13. data/lib/assets/plugins/imageselector/js/sammy-0.7.0.min.js +5 -0
  14. data/lib/assets/plugins/imageselector/js/sammy.handlebars-0.7.0.min.js +5 -0
  15. data/lib/assets/plugins/imageselector/js/sammy.template-0.7.0.min.js +5 -0
  16. data/lib/assets/plugins/imageselector/langs/en.js +4 -0
  17. data/lib/assets/plugins/imageselector/langs/en_dlg.js +4 -0
  18. data/lib/assets/plugins/imageselector/templates/no_results.hb +1 -0
  19. data/lib/assets/plugins/imageselector/templates/oembed_failed.rb +1 -0
  20. data/lib/assets/plugins/imageselector/templates/result.hb +18 -0
  21. data/lib/assets/plugins/imageselector/templates/result_thumbnail.hb +5 -0
  22. data/lib/assets/plugins/imageselector/templates/results_failed.hb +1 -0
  23. data/lib/assets/plugins/imageselector/templates/selection.hb +14 -0
  24. data/lib/assets/plugins/imageselector/templates/sizes_failed.rb +1 -0
  25. data/lib/assets/plugins/imageselector/templates/source.hb +1 -0
  26. data/lib/assets/plugins/imageselector/templates/source_next_page_link.hb +1 -0
  27. data/lib/assets/plugins/imageselector/templates/source_next_page_link_search.hb +6 -0
  28. data/lib/assets/plugins/imageselector/templates/source_search.hb +9 -0
  29. data/lib/assets/plugins/imageselector/templates/source_selected.hb +1 -0
  30. data/lib/assets/plugins/imageselector/templates/source_selected_search.hb +9 -0
  31. data/lib/assets/plugins/imageselector/templates/upload.hb +1 -0
  32. data/lib/tiny_mce_plugin_imageselector.rb +23 -0
  33. data/tiny_mce_plugin_imageselector.gemspec +15 -0
  34. metadata +100 -0
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (C) 2011 Horowhenua Library Trust
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,24 @@
1
+ = TinyMCE Plugin ImageSelector
2
+
3
+ This gem uses the tiny_mce gem's plugin system to install the https://github.com/kete/image_selector_tinymce_plugin plugin.
4
+
5
+ = Usage
6
+
7
+ To use this plugin, clone the source, gem build, gem install, and add the following after the tiny_mce gem config line:
8
+
9
+ config.gem 'tiny_mce' # if you haven't done so already
10
+ config.gem 'tiny_mce_plugin_imageselector'
11
+
12
+ You'll want to adjust your config/tiny_mce.yml file to take advantage of the plugin.
13
+
14
+ The imageselector plugin assumes that your site has imageselector config files at /javascripts/image_selector_config/. See the README at https://github.com/kete/image_selector_tinymce_plugin/ for more details on how they are set up.
15
+
16
+ = Credits
17
+
18
+ tiny_mce_plugin_imageselector was created for the Kete project (http://kete.net.nz) and is maintained by Walter McGinnis <walter a-t katipo dot co dot nz>.
19
+
20
+ = License
21
+
22
+ tiny_mce_plugin_imageselector is covered by the MIT License. See LICENSE for more information.
23
+
24
+ Copyright (C) 2011 Horowhenua Library Trust
@@ -0,0 +1,22 @@
1
+ Copyright (C) 2011 Horowhenua Library Trust
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,29 @@
1
+ # image_selector_tinymce_plugin
2
+
3
+ ## Description
4
+
5
+ Based on media_selector (https://github.com/kete/media_selector) JavaScript mini-application. See media_selector's README for dependencies, etc.
6
+
7
+ Image Selector is a jQuery and Sammy.js based mini-application for choosing from a list of providers' media assets (images in this case) that integrates into TinyMCE editor as a plugin.
8
+
9
+ Because of its dependency on jQuery, it doesn't currently work with TinyMCE's i18n translation scheme. See http://www.tinymce.com/forum/viewtopic.php?id=16761 for details. Hopefully this issue will be resolved in the future. In the meantime, translate_i18n is set to false.
10
+
11
+ ## Installation
12
+
13
+ Copy the contents of this directory to directory called imageselector under your TinyMCE install's plugins director. Configure TinyMCE to use it via the init declaration.
14
+
15
+ You'll also need to set up JSON files for your image providers and the size you want. IMPORTANT! These are assumed to be under /javascripts/image_selector_config/ on your site. See https://github.com/kete/media_selector/blob/master/data/providers.json and https://github.com/kete/media_selector/blob/master/data/sizes.json for how (you'll need to adjust URLs, etc. to suit).
16
+
17
+ ## Dependencies
18
+
19
+ TinyMCE provides the plugin API that we are integrating with.
20
+
21
+ jQuery (through a link to Google's CDN), Handlebars.js, and Sammy.js along with some Sammy.js plugins that are included in the javascripts directory.
22
+
23
+ ## Author
24
+
25
+ image_selector_tinymce_plugin was created for the Kete project (http://kete.net.nz) and is maintained by Walter McGinnis <walter a-t katipo dot co dot nz>.
26
+
27
+ ## License
28
+
29
+ image_selector_tinymce_plugin is covered by the MIT License. See LICENSE for more information.
@@ -0,0 +1,80 @@
1
+ body {
2
+ font-family: sans-serif;
3
+ }
4
+
5
+ #providers {
6
+ float: left;
7
+ width: 30%;
8
+ }
9
+
10
+ #providers ul {
11
+ padding-left: 0;
12
+ margin-left: 0;
13
+ }
14
+
15
+ #providers li.upload-link {
16
+ list-style-type: none;
17
+ }
18
+
19
+ #providers li.source {
20
+ list-style-type: none;
21
+ }
22
+
23
+ #providers li.source-selected {
24
+ list-style-type: none;
25
+ }
26
+
27
+ #results {
28
+ border-left: #999 1px solid;
29
+ padding-left: 5%;
30
+ float: right;
31
+ width: 65%;
32
+ }
33
+
34
+ #results li.result-grid {
35
+ float: left;
36
+ margin: 5px;
37
+ list-style-type:none;
38
+ width: 250px;
39
+ }
40
+
41
+ .result-thumbnail-select {
42
+ display: none;
43
+ text-align: center;
44
+ }
45
+
46
+ a.result-size-choose {
47
+ text-align: center;
48
+ }
49
+
50
+ li.size-choice {
51
+ float: left;
52
+ margin: 5px;
53
+ list-style-type:none;
54
+ }
55
+
56
+ div.size-name {
57
+ text-transform: capitalize;
58
+ }
59
+
60
+ #home-link {
61
+ clear: both;
62
+ }
63
+
64
+ textarea.selection-embed {
65
+ width: 600px;
66
+ height: 120px;
67
+ border: 3px solid #cccccc;
68
+ padding: 5px;
69
+ }
70
+
71
+ img.spinner {
72
+ display: block;
73
+ margin-left: auto;
74
+ margin-right: auto;
75
+ }
76
+
77
+ #next-page, #search-form-next {
78
+ clear: both;
79
+ width: 100%;
80
+ }
@@ -0,0 +1,48 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
2
+ <html xmlns="http://www.w3.org/1999/xhtml">
3
+ <head>
4
+ <title>Select image</title>
5
+
6
+ <link rel="stylesheet" type="text/css" href="css/media_selector.css" />
7
+
8
+ <script type="text/javascript" src="../../tiny_mce_popup.js?v={tinymce_version}"></script>
9
+
10
+ </head>
11
+ <body>
12
+
13
+ <div id="main">
14
+
15
+ <div id="providers">
16
+ <div id="providers-spinner" style="display: none"><img class="spinner" src="img/spinner.gif" width="16" height="16" alt="loading indicator. "></div>
17
+ </div>
18
+
19
+ <div id="results">
20
+ <h2 class="section-header"></h2>
21
+ <div id="results-spinner" style="display: none"><img class="spinner" src="img/spinner.gif" width="16" height="16" alt="loading indicator. "></div>
22
+ </div>
23
+
24
+ <div style="clear:both;"></div>
25
+ </div>
26
+
27
+ <div id="page-spinner"><img class="spinner" src="img/spinner.gif" width="16" height="16" alt="loading indicator. "></div>
28
+
29
+ <div class="mceActionPanel">
30
+ <input type="button" id="cancel" name="cancel" value="cancel" onclick="tinyMCEPopup.close();" />
31
+ </div>
32
+
33
+ <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
34
+
35
+ <script type="text/javascript">
36
+ jQuery.noConflict();
37
+ var mediaSelectorConfig = { directory: '/javascripts/image_selector_config/' };
38
+ </script>
39
+
40
+ <script src="js/sammy-0.7.0.min.js"></script>
41
+ <script src="js/sammy.template-0.7.0.min.js"></script>
42
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/handlebars.js/1.0.0.beta2/handlebars.min.js"></script>
43
+ <script src="js/sammy.handlebars-0.7.0.min.js"></script>
44
+
45
+ <script src="js/media_selector.js"></script>
46
+
47
+ </body>
48
+ </html>
@@ -0,0 +1 @@
1
+ (function(){tinymce.PluginManager.requireLangPack('imageselector');tinymce.create('tinymce.plugins.ImageselectorPlugin',{init:function(ed,url){ed.addCommand('mceImageselector',function(){ed.windowManager.open({file:url+'/dialog.htm',width:900+parseInt(ed.getLang('imageselector.delta_width',0)),height:500+parseInt(ed.getLang('imageselector.delta_height',0)),inline:1,translate_i18n: false},{plugin_url:url})});ed.addButton('imageselector',{title:'imageselector.desc',cmd:'mceImageselector',image:url+'/img/imageselector.png'});ed.onNodeChange.add(function(ed,cm,n){cm.setActive('imageselector',n.nodeName=='IMG')})},createControl:function(n,cm){return null},getInfo:function(){return{longname:'Imageselector plugin',author:'Walter McGinnis',authorurl:'http://waltermcginnis.com',infourl:'https://github.com/kete/image_selector_tinymce_plugin',version:"0.2"}}});tinymce.PluginManager.add('imageselector',tinymce.plugins.ImageselectorPlugin)})();
@@ -0,0 +1,81 @@
1
+ /**
2
+ * editor_plugin_src.js
3
+ *
4
+ * Copyright 2011, Horowhenua Library Trust
5
+ * Released under MIT License, see included LICENSE file
6
+ */
7
+
8
+ (function() {
9
+ // Load plugin specific language pack
10
+ tinymce.PluginManager.requireLangPack('imageselector');
11
+
12
+ tinymce.create('tinymce.plugins.ImageselectorPlugin', {
13
+ /**
14
+ * Initializes the plugin, this will be executed after the plugin has been created.
15
+ * This call is done before the editor instance has finished it's initialization so use the onInit event
16
+ * of the editor instance to intercept that event.
17
+ *
18
+ * @param {tinymce.Editor} ed Editor instance that the plugin is initialized in.
19
+ * @param {string} url Absolute URL to where the plugin is located.
20
+ */
21
+ init : function(ed, url) {
22
+ // Register the command so that it can be invoked by using tinyMCE.activeEditor.execCommand('mceImageselector');
23
+ ed.addCommand('mceImageselector', function() {
24
+ ed.windowManager.open({
25
+ file : url + '/dialog.htm',
26
+ width : 900 + parseInt(ed.getLang('imageselector.delta_width', 0)),
27
+ height : 500 + parseInt(ed.getLang('imageselector.delta_height', 0)),
28
+ inline : 1,
29
+ translate_i18n: false
30
+ }, {
31
+ plugin_url : url // Plugin absolute URL
32
+ });
33
+ });
34
+
35
+ // Register imageselector button
36
+ ed.addButton('imageselector', {
37
+ title : 'imageselector.desc',
38
+ cmd : 'mceImageselector',
39
+ image : url + '/img/imageselector.png'
40
+ });
41
+
42
+ // Add a node change handler, selects the button in the UI when a image is selected
43
+ ed.onNodeChange.add(function(ed, cm, n) {
44
+ cm.setActive('imageselector', n.nodeName == 'IMG');
45
+ });
46
+ },
47
+
48
+ /**
49
+ * Creates control instances based in the incomming name. This method is normally not
50
+ * needed since the addButton method of the tinymce.Editor class is a more easy way of adding buttons
51
+ * but you sometimes need to create more complex controls like listboxes, split buttons etc then this
52
+ * method can be used to create those.
53
+ *
54
+ * @param {String} n Name of the control to create.
55
+ * @param {tinymce.ControlManager} cm Control manager to use inorder to create new control.
56
+ * @return {tinymce.ui.Control} New control instance or null if no control was created.
57
+ */
58
+ createControl : function(n, cm) {
59
+ return null;
60
+ },
61
+
62
+ /**
63
+ * Returns information about the plugin as a name/value array.
64
+ * The current keys are longname, author, authorurl, infourl and version.
65
+ *
66
+ * @return {Object} Name/value array containing information about the plugin.
67
+ */
68
+ getInfo : function() {
69
+ return {
70
+ longname : 'Imageselector plugin',
71
+ author : 'Walter McGinnis',
72
+ authorurl : 'http://waltermcginnis.com',
73
+ infourl : 'https://github.com/kete/image_selector_tinymce_plugin',
74
+ version : "0.2"
75
+ };
76
+ }
77
+ });
78
+
79
+ // Register plugin
80
+ tinymce.PluginManager.add('imageselector', tinymce.plugins.ImageselectorPlugin);
81
+ })();
@@ -0,0 +1,562 @@
1
+ /**
2
+ * media_selector.js
3
+ *
4
+ * Copyright 2011, Horowhenua Library Trust
5
+ * Released under MIT License, see included LICENSE file
6
+ *
7
+ * expects a mediaSelectorConfig object to be defined before this file is run
8
+ * it should look something like this:
9
+ *
10
+ * { directory: path/to/directory/for/config/files/ }
11
+ *
12
+ */
13
+ (function($) {
14
+ var app = $.sammy('#main', function() {
15
+ this.use(Sammy.Handlebars, 'hb');
16
+
17
+ this.helpers({
18
+ stubUrlForResult: function() {
19
+ return document.URL.split('#')[0] + '#/results/'
20
+ },
21
+ providerListIdFor: function(providerIndex) {
22
+ return 'provider-list-' + providerIndex;
23
+ },
24
+ providerTitleIdFor: function(providerIndex) {
25
+ return 'provider-title-' + providerIndex;
26
+ },
27
+ setProviders: function(context) {
28
+ $('#providers-spinner').hide();
29
+
30
+ $.each(context.providers, function(i, provider) {
31
+ $('#providers').append("<h3 class=\"provider-title\" id=\"" + context.providerTitleIdFor(i) + "\">" + provider.title + '</h3>');
32
+ $('#providers').append("<ul id=\"" + context.providerListIdFor(i) + "\"></div>");
33
+ });
34
+ },
35
+ sourceIdFor: function(providerIndex, sourceIndex) {
36
+ return 'source-' + providerIndex + '-' + sourceIndex;
37
+ },
38
+ sourceTemplateStub: 'templates/source'
39
+ });
40
+
41
+ // load providers data for every request
42
+ this.around(function(callback) {
43
+ var context = this;
44
+ this.load(mediaSelectorConfig.directory + 'providers.json')
45
+ .then(function(providers) {
46
+ context.providers = providers;
47
+ })
48
+ .then(callback);
49
+ });
50
+
51
+ // index has two panes; providers with their sources and results for selected source
52
+ // which in index case (none specified) is first source of first provider
53
+ this.get('#/', function(context) {
54
+ // get the results from default_source
55
+ // assumes that first provider's first source isn't a search
56
+ var defaultSource = this.providers[0].sources[0];
57
+
58
+ defaultSource.sourceId = context.sourceIdFor(0, 0);
59
+
60
+ defaultSource['provider_title'] = this.providers[0]['title'];
61
+
62
+ this.trigger('updateSourceTo', defaultSource);
63
+
64
+ this.trigger('updateResultsFor', defaultSource);
65
+ });
66
+
67
+ // results for a give source id
68
+ // selected source indicated in providers source list
69
+ function getSource(context) {
70
+ var idIndexes = context.params['id'].split('-');
71
+ var providerIndex = idIndexes[1],
72
+ sourceIndex = idIndexes[2],
73
+ the_provider = context.providers[providerIndex];
74
+
75
+ // get the results from selected source
76
+ var selectedSource = the_provider.sources[sourceIndex];
77
+
78
+ selectedSource['provider_title'] = the_provider['title'];
79
+
80
+ context.trigger('updateSourceTo', selectedSource);
81
+
82
+ context.trigger('updateResultsFor', selectedSource);
83
+ }
84
+
85
+ // these are the same, except for addition of pagination in route
86
+ this.get('#/:id', function(context) { getSource(context) });
87
+ this.get('#/:id/page/:page_number', function(context) { getSource(context) });
88
+
89
+ // same as above, but target for search form for a source
90
+ function postSearchOfSource(context) {
91
+ var idIndexes = context.params['id'].split('-');
92
+ var providerIndex = idIndexes[1],
93
+ sourceIndex = idIndexes[2],
94
+ the_provider = context.providers[providerIndex];
95
+
96
+ // get the results from selected source
97
+ var selectedSource = the_provider.sources[sourceIndex];
98
+
99
+ selectedSource['provider_title'] = the_provider['title'];
100
+
101
+ context.trigger('updateSourceTo', selectedSource);
102
+
103
+ context.trigger('updateResultsFor', selectedSource);
104
+ }
105
+
106
+ // same except for pagination support
107
+ this.post('#/:id', function(context) { postSearchOfSource(context) });
108
+ this.post('#/:id/page/:page_number', function(context) { postSearchOfSource(context) });
109
+
110
+ // a given result's display
111
+ // show available sizes (based on requests to oembed provider for result id)
112
+ // so user may choose which size
113
+ this.get('#/results/:id', function(context) {
114
+ $('#results').fadeOut();
115
+ $('#providers').fadeOut();
116
+ $('#page-spinner').fadeIn();
117
+
118
+ // params['id'] decodes to normal url, but we need escaped version
119
+ var resultUrl = escape(this.params['id']);
120
+
121
+ var result = {
122
+ url: resultUrl,
123
+ hasAllSizes: false,
124
+ hasSizesError: false,
125
+ render: function(context) {
126
+ this.updateWithNeededSizesThenRender(context);
127
+ },
128
+ updateWithNeededSizesThenRender: function(context) {
129
+ result = this;
130
+
131
+ if (result.hasAllSizes || result.hasSizesError) {
132
+ if (!(result.hasSizeError)) {
133
+ // rearrange result to be an array
134
+ // so we don't need to know about the keys of sizes in the template
135
+ var resultSizes = [],
136
+ resultTitle = '',
137
+ resultAuthor= {};
138
+
139
+ var sizeCount = 0;
140
+
141
+ $.each(sizesNames(context), function(i, sizeName) {
142
+ result[sizeName]["size_name"] = sizeName;
143
+
144
+ var currentSize = {};
145
+
146
+ $.each(context.sizes, function(i, sizeOption) {
147
+ if (sizeName === sizeOption.name) {
148
+ currentSize = sizeOption;
149
+ return false;
150
+ }
151
+ });
152
+
153
+ result[sizeName]["oembed_url"] = encodeURIComponent(result.oembedUrlFor(currentSize));
154
+
155
+ resultSizes.push(result[sizeName]);
156
+
157
+ sizeCount++;
158
+
159
+ if (sizeCount === 1) {
160
+ resultTitle = result[sizeName].title;
161
+
162
+ resultAuthor = {
163
+ url: result[sizeName].author_url,
164
+ name: result[sizeName].author_name
165
+ };
166
+ }
167
+ });
168
+
169
+ var resultForTemplate = {
170
+ result_title: resultTitle,
171
+ result_author_url: resultAuthor.url,
172
+ result_author_name: resultAuthor.name,
173
+ sizes: resultSizes
174
+ };
175
+
176
+ $('#page-spinner').fadeOut();
177
+ // this views takes whole area of page
178
+ context.partial('templates/result.hb', resultForTemplate);
179
+ }
180
+ } else {
181
+ // check sizes that are already complete
182
+ var neededSizes = [];
183
+
184
+ $.each(sizesNames(context), function(i, sizeName) {
185
+ // if undefined for sizeName
186
+ // push to neededSizes
187
+ if (typeof(result[sizeName]) == "undefined") {
188
+ neededSizes.push(sizeName);
189
+ }
190
+ });
191
+
192
+ if (neededSizes.length === 0) {
193
+ // this will complete recursion during next call
194
+ result.hasAllSizes = true;
195
+ result.updateWithNeededSizesThenRender(context);
196
+ } else {
197
+ // get the first in neededSizes
198
+ var nextSizeName = neededSizes.shift();
199
+ size = {};
200
+
201
+ $.each(context.sizes, function(i, sizeOption) {
202
+ if (nextSizeName === sizeOption.name) {
203
+ size = sizeOption;
204
+ return false;
205
+ }
206
+ });
207
+
208
+ // and append what is returned from oembed to result object for that size name
209
+ $.get(result.oembedUrlFor(size))
210
+ .success(function(response) {
211
+ // TODO: make this detect xml or json and parse accordingly
212
+ // TODO: this is limited to same domain only for now, update to handle JSONP
213
+ // probably need to switch to $.ajax and more complete parameters call for jsonp
214
+ result[size.name] = $.parseJSON(response);
215
+
216
+ // scope issue, response is set in calling scope, and not getting set with recursive call
217
+ result.updateWithNeededSizesThenRender(context);
218
+ })
219
+ .error(function() {
220
+ context.log("oembed response failed for " + size.name);
221
+ context.partial('templates/oembed_failed.hb', { oembed_url: oembedUrlFor(size) } );
222
+ // this will break cycle of recursive calls
223
+ result.hasSizesError = true;
224
+ result.updateWithNeededSizesThenRender(context);
225
+ });
226
+ }
227
+ }
228
+ },
229
+ oembedUrlFor: function(size) {
230
+ var oembed_url = result.provider.oembed_endpoint + '?url=' + result.url;
231
+ oembed_url += '&maxwidth=' + size.width;
232
+ oembed_url += '&maxheight=' + size.height;
233
+ return oembed_url;
234
+ }
235
+ };
236
+
237
+ // look up provider oembed endpoint
238
+ // TODO: replace this with something that doesn't iterate through each provider
239
+ $.each(context.providers, function(i, provider) {
240
+ // TODO: make sure this works with IE8
241
+ if (result.url.indexOf(provider.domain) != -1) {
242
+ result["provider"] = provider;
243
+ return false;
244
+ }
245
+ });
246
+
247
+ $.when(sizesLoadedInto(context))
248
+ .then(function() {
249
+ result.render(context)
250
+ });
251
+
252
+ });
253
+
254
+ // this gives selected result and the user's selected size
255
+ // and outputs end result of oembed request for html to embed the result at the selected size
256
+ this.get('#/selections/:id', function(context) {
257
+ $('#result-description-and-sizes').fadeOut();
258
+ $('#page-spinner').fadeIn();
259
+
260
+ // params['id'] decodes to normal url, but we need escaped version
261
+ var oembedUrl = this.params['id'];
262
+
263
+ $.get(oembedUrl)
264
+ .success(function(response) {
265
+ // TODO: make this detect xml or json and parse accordingly
266
+ // TODO: this is limited to same domain only for now, update to handle JSONP
267
+ // probably need to switch to $.ajax and more complete parameters call for jsonp
268
+ var selectionFromResponse = $.parseJSON(response);
269
+
270
+ // add alt value for selection so we can use it in template
271
+ var alt = selectionFromResponse.title;
272
+
273
+ // add a full stop to title for better accessibility
274
+ // start by stripping off trailing spaces for ease our following logic
275
+ alt = alt.replace(/\s+$/g, "");
276
+
277
+ if (alt.charAt( alt.length-1 ) === ".") {
278
+ alt = alt + '. ';
279
+ }
280
+
281
+ selectionFromResponse.alt = alt;
282
+
283
+ // look up matching provider, check for code to call with selectionFromResponse
284
+ var selectionProvider;
285
+
286
+ $.each(context.providers, function(i, provider) {
287
+ // TODO: make sure this works with IE8
288
+ if (oembedUrl.indexOf(provider.domain) != -1) {
289
+ selectionProvider = provider;
290
+ return false;
291
+ }
292
+ });
293
+
294
+ $('#page-spinner').fadeOut();
295
+
296
+ if (selectionProvider !== '' &&
297
+ typeof selectionProvider !== 'undefined' &&
298
+ selectionProvider.insertIntoEditor !== '' &&
299
+ typeof selectionProvider.insertIntoEditor !== 'undefined') {
300
+
301
+ if (selectionProvider.insertIntoEditor.editor !== '' &&
302
+ typeof selectionProvider.insertIntoEditor.editor !== 'undefined') {
303
+
304
+ // TODO: tweak this based on provider.media_type in future
305
+ var valueToInsert = '<img src="' + selectionFromResponse.url + '" width="';
306
+ valueToInsert = valueToInsert + selectionFromResponse.width;
307
+ valueToInsert = valueToInsert + '" height="' + selectionFromResponse.height;
308
+ valueToInsert = valueToInsert + '" alt="' + selectionFromResponse.alt + '"> by <a href="';
309
+ valueToInsert = valueToInsert + selectionFromResponse.author_url + '">' + selectionFromResponse.author_name +'</a>';
310
+
311
+ if (selectionProvider.insertIntoEditor.editor === 'TinyMCE') {
312
+ tinyMCEPopup.editor.execCommand('mceInsertContent', false, valueToInsert);
313
+ tinyMCEPopup.close();
314
+ }
315
+ }
316
+ selectionProvider.processFinal(selectionFromResponse);
317
+
318
+ } else {
319
+ // otherwise we render template
320
+ // this view takes whole of view
321
+ context.partial('templates/selection.hb', selectionFromResponse);
322
+ }
323
+ })
324
+ .error(function() {
325
+ context.log("oembed selection response failed for " + oembedUrl);
326
+ context.partial('templates/oembed_failed.hb', { oembed_url: oembedUrl });
327
+ });
328
+ });
329
+
330
+ function sizesLoadedInto(context) {
331
+ return $.get(mediaSelectorConfig.directory + 'sizes.json')
332
+ .success(function(response) {
333
+ // plain response is probably already json object if parse returns null
334
+ var responseAsJSON = response;
335
+
336
+ if (typeof(responseAsJSON) == 'string' || responseAsJSON === '' || typeof(responseAsJSON) === 'undefined') {
337
+ responseAsJSON = $.parseJSON(response);
338
+ }
339
+
340
+ context.sizes = responseAsJSON;
341
+ })
342
+ .error(function() {
343
+ context.log("response failed");
344
+ context.partial('templates/sizes_failed.hb');
345
+ });
346
+ }
347
+
348
+ function sizesNames(context) {
349
+ var names = [];
350
+ $.each(context.sizes, function(i, size) {
351
+ names.push(size.name);
352
+ });
353
+ return names;
354
+ }
355
+
356
+ // route = #/id (id of source) - in form provider index - source index
357
+ // i.e. 0-1 would be the first provider's second source
358
+ // route = #/id (id of source) + params[search_terms] for searchable source
359
+ // route for results = #/results/id where id is url
360
+ // add to results:
361
+ // or upload image -> requests url for upload and returns to #/results/id
362
+ // or add image URL
363
+ // add to result detail page a "back" button
364
+ this.bind('updateSourceTo', function(e, selectedSource) {
365
+ $('#page-spinner').hide();
366
+ $('#results-list').text('');
367
+ $('h3.no-results-title').hide();
368
+ $('#results-spinner').fadeIn();
369
+
370
+ context = this;
371
+
372
+ var searchTerms = this.params['search_terms'];
373
+
374
+ if ($('h3.provider-title').length === 0) {
375
+ this.setProviders(context);
376
+ }
377
+
378
+ $.each(this.providers, function(i, provider) {
379
+ var providerIndex = i;
380
+ var providerListId = context.providerListIdFor(providerIndex);
381
+
382
+ $('#' + providerListId).text('');
383
+
384
+ $.each(provider.sources, function(i, source) {
385
+ source.sourceId = context.sourceIdFor(providerIndex, i);
386
+
387
+ var appropriate_template = context.sourceTemplateStub;
388
+
389
+ if (selectedSource && source === selectedSource) {
390
+ appropriate_template += '_selected';
391
+ }
392
+
393
+ if (source.searchable_stub) {
394
+ appropriate_template += '_search';
395
+
396
+ if (searchTerms) {
397
+ source.value = searchTerms;
398
+ } else {
399
+ source.value = '';
400
+ }
401
+ }
402
+
403
+ appropriate_template += '.hb';
404
+
405
+ context.render(appropriate_template, source)
406
+ .appendTo("#" + providerListId);
407
+ });
408
+
409
+ // HACK: for some reason when '&target_service=' + targetService is added, handlebars fails to parse {{url}} correctly
410
+ // this worksaround the issue in an ugly fashion
411
+ provider.upload_startpoint['service_target_param'] = '&service_target=' + escape(context.stubUrlForResult());
412
+
413
+ if (typeof(provider.upload_startpoint) != "undefined") {
414
+ context.render('templates/upload.hb', provider.upload_startpoint)
415
+ .appendTo("#" + providerListId);
416
+ }
417
+ });
418
+ });
419
+
420
+ // get each of the entries up to limit
421
+ // render in template for result
422
+ // append to #results
423
+ // animate the results each being added
424
+ this.bind('updateResultsFor', function(e, source) {
425
+ context = this;
426
+
427
+ var searchTerms = this.params['search_terms'],
428
+ pageNumber = this.params['page_number'],
429
+ fullUrl = source.url;
430
+
431
+ if (searchTerms) {
432
+ fullUrl += searchTerms;
433
+ }
434
+
435
+ if (typeof(source['limit_parameter']) !== "undefined" &&
436
+ typeof(source['display_limit']) !== "undefined") {
437
+
438
+ var limit_parameter_adjusted = source.limit_parameter;
439
+
440
+ if (searchTerms) {
441
+ limit_parameter_adjusted = limit_parameter_adjusted.replace(/^\?/, "&");
442
+ }
443
+
444
+ fullUrl += limit_parameter_adjusted + source.display_limit;
445
+
446
+ if (source['page_parameter'] !== '' && typeof(source['page_parameter']) !== "undefined") {
447
+ if (pageNumber === '' || typeof pageNumber === 'undefined') {
448
+ pageNumber = 1;
449
+ }
450
+
451
+ source.nextPage = parseInt(pageNumber) + 1;
452
+ fullUrl += source.page_parameter + pageNumber.toString();
453
+ }
454
+ }
455
+
456
+ var resultRequest = $.get(fullUrl)
457
+ .success(function( response ) {
458
+ $('#results-spinner').hide();
459
+ $('#results-list').text('');
460
+
461
+ var resultsTitle = source.name + ' ' + source.media_type_plural;
462
+
463
+ if (searchTerms) {
464
+ resultsTitle = resultsTitle + ' for "' + searchTerms + '"';
465
+ source.searchTerms = searchTerms;
466
+ }
467
+
468
+ resultsTitle = resultsTitle + '(click to select)';
469
+
470
+ $('#results h2').text(resultsTitle);
471
+
472
+ var itemsLimit = source.display_limit;
473
+ var itemsCount = 0;
474
+
475
+ // items from rss items or atom entries
476
+ // i want image thumbnail src (enclosure or media:thumbnail)
477
+ // title
478
+ // full url (link)
479
+ var items = $(response).find('item');
480
+
481
+ // handle Atom
482
+ if (items.length === 0) {
483
+ items = $(response).find('entry');
484
+ }
485
+
486
+ if (items.length) {
487
+ $('#results').append("<ul id=\"results-list\">");
488
+
489
+ items.each(function() {
490
+ if (itemsCount === itemsLimit) { return false; }
491
+
492
+ var resultThumbnail = $(this).find("thumbnail").attr('url');
493
+
494
+ if (!resultThumbnail || 0 === resultThumbnail.length) {
495
+ // TODO: currently broken
496
+ $(this).find('description').each(function() {
497
+ resultThumbnail = $(this).find('img').attr('src');
498
+ });
499
+ }
500
+
501
+ var resultTitle = $(this).find('title').text();
502
+ var resultLink = $(this).find('link').text();
503
+
504
+ var result = {
505
+ title: resultTitle,
506
+ link: resultLink,
507
+ link_escaped: encodeURIComponent(resultLink),
508
+ thumbnail_url: resultThumbnail
509
+ }
510
+
511
+ context.render('templates/result_thumbnail.hb', result)
512
+ .appendTo('#results-list');
513
+
514
+ itemsCount++;
515
+ });
516
+
517
+ $('#results').append("</ul>");
518
+
519
+ if (source.nextPage !== '' && typeof source.nextPage !== 'undefined') {
520
+ // clear them, if they exist, so we can replace them later
521
+ $('#search-form-next').remove();
522
+ $('#next-page').remove();
523
+
524
+ // if we have less items than display_limit,
525
+ // we don't have any more pages of results
526
+ // if equal, we assume there are more (faulty assumption, but relatively safe)
527
+ if (items.length === source.display_limit) {
528
+ if (searchTerms) {
529
+ context.render('templates/source_next_page_link_search.hb', source)
530
+ .appendTo('#results');
531
+ } else {
532
+ context.render('templates/source_next_page_link.hb', source)
533
+ .appendTo('#results');
534
+ }
535
+ }
536
+ }
537
+ } else {
538
+ context.render('templates/no_results.hb', source)
539
+ .appendTo($('#results'));
540
+ }
541
+ })
542
+ .error(function() {
543
+ $('#results-spinner').hide();
544
+
545
+ // clear them, as they are no longer relevant
546
+ $('#search-form-next').remove();
547
+ $('#next-page').remove();
548
+
549
+ context.render('templates/results_failed.hb', source)
550
+ .appendTo($('#results'));
551
+ });
552
+ });
553
+
554
+
555
+ });
556
+
557
+ $(function() {
558
+ app.run('#/');
559
+ });
560
+
561
+ })(jQuery);
562
+
@@ -0,0 +1,5 @@
1
+ // -- Sammy.js -- /sammy.js
2
+ // http://sammyjs.org
3
+ // Version: 0.7.0
4
+ // Built: 2011-07-30 16:55:53 -0700
5
+ (function(h,j){var p,g="([^/]+)",k=/:([\w\d]+)/g,l=/\?([^#]*)$/,c=function(q){return Array.prototype.slice.call(q)},d=function(q){return Object.prototype.toString.call(q)==="[object Function]"},m=function(q){return Object.prototype.toString.call(q)==="[object Array]"},i=function(q){return decodeURIComponent((q||"").replace(/\+/g," "))},b=encodeURIComponent,f=function(q){return String(q).replace(/&(?!\w+;)/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},n=function(q){return function(r,s){return this.route.apply(this,[q,r,s])}},a={},o=!!(j.history&&history.pushState),e=[];p=function(){var r=c(arguments),s,q;p.apps=p.apps||{};if(r.length===0||r[0]&&d(r[0])){return p.apply(p,["body"].concat(r))}else{if(typeof(q=r.shift())=="string"){s=p.apps[q]||new p.Application();s.element_selector=q;if(r.length>0){h.each(r,function(t,u){s.use(u)})}if(s.element_selector!=q){delete p.apps[q]}p.apps[s.element_selector]=s;return s}}};p.VERSION="0.7.0";p.addLogger=function(q){e.push(q)};p.log=function(){var q=c(arguments);q.unshift("["+Date()+"]");h.each(e,function(s,r){r.apply(p,q)})};if(typeof j.console!="undefined"){if(d(j.console.log.apply)){p.addLogger(function(){j.console.log.apply(j.console,arguments)})}else{p.addLogger(function(){j.console.log(arguments)})}}else{if(typeof console!="undefined"){p.addLogger(function(){console.log.apply(console,arguments)})}}h.extend(p,{makeArray:c,isFunction:d,isArray:m});p.Object=function(q){return h.extend(this,q||{})};h.extend(p.Object.prototype,{escapeHTML:f,h:f,toHash:function(){var q={};h.each(this,function(s,r){if(!d(r)){q[s]=r}});return q},toHTML:function(){var q="";h.each(this,function(s,r){if(!d(r)){q+="<strong>"+s+"</strong> "+r+"<br />"}});return q},keys:function(q){var r=[];for(var s in this){if(!d(this[s])||!q){r.push(s)}}return r},has:function(q){return this[q]&&h.trim(this[q].toString())!==""},join:function(){var r=c(arguments);var q=r.shift();return r.join(q)},log:function(){p.log.apply(p,arguments)},toString:function(q){var r=[];h.each(this,function(t,s){if(!d(s)||q){r.push('"'+t+'": '+s.toString())}});return"Sammy.Object: {"+r.join(",")+"}"}});p.DefaultLocationProxy=function(r,q){this.app=r;this.is_native=false;this.has_history=o;this._startPolling(q)};p.DefaultLocationProxy.fullPath=function(q){var r=q.toString().match(/^[^#]*(#.+)$/);var s=r?r[1]:"";return[q.pathname,q.search,s].join("")};p.DefaultLocationProxy.prototype={bind:function(){var r=this,s=this.app,q=p.DefaultLocationProxy;h(j).bind("hashchange."+this.app.eventNamespace(),function(u,t){if(r.is_native===false&&!t){r.is_native=true;j.clearInterval(q._interval)}s.trigger("location-changed")});if(o&&!s.disable_push_state){h(j).bind("popstate."+this.app.eventNamespace(),function(t){s.trigger("location-changed")});h("a").live("click.history-"+this.app.eventNamespace(),function(u){var t=q.fullPath(this);if(this.hostname==j.location.hostname&&s.lookupRoute("get",t)){u.preventDefault();r.setLocation(t);return false}})}if(!q._bindings){q._bindings=0}q._bindings++},unbind:function(){h(j).unbind("hashchange."+this.app.eventNamespace());h(j).unbind("popstate."+this.app.eventNamespace());h("a").die("click.history-"+this.app.eventNamespace());p.DefaultLocationProxy._bindings--;if(p.DefaultLocationProxy._bindings<=0){j.clearInterval(p.DefaultLocationProxy._interval)}},getLocation:function(){return p.DefaultLocationProxy.fullPath(j.location)},setLocation:function(q){if(/^([^#\/]|$)/.test(q)){if(o){q="/"+q}else{q="#!/"+q}}if(q!=this.getLocation()){if(o&&/^\//.test(q)){history.pushState({path:q},j.title,q);this.app.trigger("location-changed")}else{return(j.location=q)}}},_startPolling:function(s){var r=this;if(!p.DefaultLocationProxy._interval){if(!s){s=10}var q=function(){var t=r.getLocation();if(typeof p.DefaultLocationProxy._last_location=="undefined"||t!=p.DefaultLocationProxy._last_location){j.setTimeout(function(){h(j).trigger("hashchange",[true])},0)}p.DefaultLocationProxy._last_location=t};q();p.DefaultLocationProxy._interval=j.setInterval(q,s)}}};p.Application=function(q){var r=this;this.routes={};this.listeners=new p.Object({});this.arounds=[];this.befores=[];this.namespace=(new Date()).getTime()+"-"+parseInt(Math.random()*1000,10);this.context_prototype=function(){p.EventContext.apply(this,arguments)};this.context_prototype.prototype=new p.EventContext();if(d(q)){q.apply(this,[this])}if(!this._location_proxy){this.setLocationProxy(new p.DefaultLocationProxy(this,this.run_interval_every))}if(this.debug){this.bindToAllEvents(function(t,s){r.log(r.toString(),t.cleaned_type,s||{})})}};p.Application.prototype=h.extend({},p.Object.prototype,{ROUTE_VERBS:["get","post","put","delete"],APP_EVENTS:["run","unload","lookup-route","run-route","route-found","event-context-before","event-context-after","changed","error","check-form-submission","redirect","location-changed"],_last_route:null,_location_proxy:null,_running:false,element_selector:"body",debug:false,raise_errors:false,run_interval_every:50,disable_push_state:false,template_engine:null,toString:function(){return"Sammy.Application:"+this.element_selector},$element:function(q){return q?h(this.element_selector).find(q):h(this.element_selector)},use:function(){var q=c(arguments),s=q.shift(),r=s||"";try{q.unshift(this);if(typeof s=="string"){r="Sammy."+s;s=p[s]}s.apply(this,q)}catch(t){if(typeof s==="undefined"){this.error("Plugin Error: called use() but plugin ("+r.toString()+") is not defined",t)}else{if(!d(s)){this.error("Plugin Error: called use() but '"+r.toString()+"' is not a function",t)}else{this.error("Plugin Error",t)}}}return this},setLocationProxy:function(q){var r=this._location_proxy;this._location_proxy=q;if(this.isRunning()){if(r){r.unbind()}this._location_proxy.bind()}},route:function(u,r,w){var t=this,v=[],q,s;if(!w&&d(r)){r=u;w=r;u="any"}u=u.toLowerCase();if(r.constructor==String){k.lastIndex=0;while((s=k.exec(r))!==null){v.push(s[1])}r=new RegExp(r.replace(k,g)+"$")}if(typeof w=="string"){w=t[w]}q=function(x){var y={verb:x,path:r,callback:w,param_names:v};t.routes[x]=t.routes[x]||[];t.routes[x].push(y)};if(u==="any"){h.each(this.ROUTE_VERBS,function(y,x){q(x)})}else{q(u)}return this},get:n("get"),post:n("post"),put:n("put"),del:n("delete"),any:n("any"),mapRoutes:function(r){var q=this;h.each(r,function(s,t){q.route.apply(q,t)});return this},eventNamespace:function(){return["sammy-app",this.namespace].join("-")},bind:function(q,s,u){var t=this;if(typeof u=="undefined"){u=s}var r=function(){var x,v,w;x=arguments[0];w=arguments[1];if(w&&w.context){v=w.context;delete w.context}else{v=new t.context_prototype(t,"bind",x.type,w,x.target)}x.cleaned_type=x.type.replace(t.eventNamespace(),"");u.apply(v,[x,w])};if(!this.listeners[q]){this.listeners[q]=[]}this.listeners[q].push(r);if(this.isRunning()){this._listen(q,r)}return this},trigger:function(q,r){this.$element().trigger([q,this.eventNamespace()].join("."),[r]);return this},refresh:function(){this.last_location=null;this.trigger("location-changed");return this},before:function(q,r){if(d(q)){r=q;q={}}this.befores.push([q,r]);return this},after:function(q){return this.bind("event-context-after",q)},around:function(q){this.arounds.push(q);return this},isRunning:function(){return this._running},helpers:function(q){h.extend(this.context_prototype.prototype,q);return this},helper:function(q,r){this.context_prototype.prototype[q]=r;return this},run:function(q){if(this.isRunning()){return false}var r=this;h.each(this.listeners.toHash(),function(s,t){h.each(t,function(v,u){r._listen(s,u)})});this.trigger("run",{start_url:q});this._running=true;this.last_location=null;if(!(/\#(.+)/.test(this.getLocation()))&&typeof q!="undefined"){this.setLocation(q)}this._checkLocation();this._location_proxy.bind();this.bind("location-changed",function(){r._checkLocation()});this.bind("submit",function(t){var s=r._checkFormSubmission(h(t.target).closest("form"));return(s===false)?t.preventDefault():false});h(j).bind("beforeunload",function(){r.unload()});return this.trigger("changed")},unload:function(){if(!this.isRunning()){return false}var q=this;this.trigger("unload");this._location_proxy.unbind();this.$element().unbind("submit").removeClass(q.eventNamespace());h.each(this.listeners.toHash(),function(r,s){h.each(s,function(u,t){q._unlisten(r,t)})});this._running=false;return this},bindToAllEvents:function(r){var q=this;h.each(this.APP_EVENTS,function(s,t){q.bind(t,r)});h.each(this.listeners.keys(true),function(t,s){if(h.inArray(s,q.APP_EVENTS)==-1){q.bind(s,r)}});return this},routablePath:function(q){return q.replace(l,"")},lookupRoute:function(w,u){var v=this,t=false,s=0,q,r;if(typeof this.routes[w]!="undefined"){q=this.routes[w].length;for(;s<q;s++){r=this.routes[w][s];if(v.routablePath(u).match(r.path)){t=r;break}}}return t},runRoute:function(s,F,u,x){var t=this,D=this.lookupRoute(s,F),r,A,v,z,E,B,y,C,q;this.log("runRoute",[s,F].join(" "));this.trigger("run-route",{verb:s,path:F,params:u});if(typeof u=="undefined"){u={}}h.extend(u,this._parseQueryString(F));if(D){this.trigger("route-found",{route:D});if((C=D.path.exec(this.routablePath(F)))!==null){C.shift();h.each(C,function(G,H){if(D.param_names[G]){u[D.param_names[G]]=i(H)}else{if(!u.splat){u.splat=[]}u.splat.push(i(H))}})}r=new this.context_prototype(this,s,F,u,x);v=this.arounds.slice(0);E=this.befores.slice(0);y=[r].concat(u.splat);A=function(){var G;while(E.length>0){B=E.shift();if(t.contextMatchesOptions(r,B[0])){G=B[1].apply(r,[r]);if(G===false){return false}}}t.last_route=D;r.trigger("event-context-before",{context:r});G=D.callback.apply(r,y);r.trigger("event-context-after",{context:r});return G};h.each(v.reverse(),function(G,H){var I=A;A=function(){return H.apply(r,[I])}});try{q=A()}catch(w){this.error(["500 Error",s,F].join(" "),w)}return q}else{return this.notFound(s,F)}},contextMatchesOptions:function(t,v,r){var s=v;if(typeof s==="undefined"||s=={}){return true}if(typeof r==="undefined"){r=true}if(typeof s==="string"||d(s.test)){s={path:s}}if(s.only){return this.contextMatchesOptions(t,s.only,true)}else{if(s.except){return this.contextMatchesOptions(t,s.except,false)}}var q=true,u=true;if(s.path){if(!d(s.path.test)){s.path=new RegExp(s.path.toString()+"$")}q=s.path.test(t.path)}if(s.verb){if(typeof s.verb==="string"){u=s.verb===t.verb}else{u=s.verb.indexOf(t.verb)>-1}}return r?(u&&q):!(u&&q)},getLocation:function(){return this._location_proxy.getLocation()},setLocation:function(q){return this._location_proxy.setLocation(q)},swap:function(q){return this.$element().html(q)},templateCache:function(q,r){if(typeof r!="undefined"){return a[q]=r}else{return a[q]}},clearTemplateCache:function(){return a={}},notFound:function(s,r){var q=this.error(["404 Not Found",s,r].join(" "));return(s==="get")?q:true},error:function(r,q){if(!q){q=new Error()}q.message=[r,q.message].join(" ");this.trigger("error",{message:q.message,error:q});if(this.raise_errors){throw (q)}else{this.log(q.message,q)}},_checkLocation:function(){var q,r;q=this.getLocation();if(!this.last_location||this.last_location[0]!="get"||this.last_location[1]!=q){this.last_location=["get",q];r=this.runRoute("get",q)}return r},_getFormVerb:function(s){var r=h(s),t,q;q=r.find('input[name="_method"]');if(q.length>0){t=q.val()}if(!t){t=r[0].getAttribute("method")}if(!t||t==""){t="get"}return h.trim(t.toString().toLowerCase())},_checkFormSubmission:function(s){var q,t,v,u,r;this.trigger("check-form-submission",{form:s});q=h(s);t=q.attr("action")||"";v=this._getFormVerb(q);this.log("_checkFormSubmission",q,t,v);if(v==="get"){this.setLocation(t+"?"+this._serializeFormParams(q));r=false}else{u=h.extend({},this._parseFormParams(q));r=this.runRoute(v,t,u,s.get(0))}return(typeof r=="undefined")?false:r},_serializeFormParams:function(r){var t="",q=r.serializeArray(),s;if(q.length>0){t=this._encodeFormPair(q[0].name,q[0].value);for(s=1;s<q.length;s++){t=t+"&"+this._encodeFormPair(q[s].name,q[s].value)}}return t},_encodeFormPair:function(q,r){return b(q)+"="+b(r)},_parseFormParams:function(q){var t={},s=q.serializeArray(),r;for(r=0;r<s.length;r++){t=this._parseParamPair(t,s[r].name,s[r].value)}return t},_parseQueryString:function(t){var v={},s,r,u,q;s=t.match(l);if(s){r=s[1].split("&");for(q=0;q<r.length;q++){u=r[q].split("=");v=this._parseParamPair(v,i(u[0]),i(u[1]||""))}}return v},_parseParamPair:function(s,q,r){if(s[q]){if(m(s[q])){s[q].push(r)}else{s[q]=[s[q],r]}}else{s[q]=r}return s},_listen:function(q,r){return this.$element().bind([q,this.eventNamespace()].join("."),r)},_unlisten:function(q,r){return this.$element().unbind([q,this.eventNamespace()].join("."),r)}});p.RenderContext=function(q){this.event_context=q;this.callbacks=[];this.previous_content=null;this.content=null;this.next_engine=false;this.waiting=false};p.RenderContext.prototype=h.extend({},p.Object.prototype,{then:function(s){if(!d(s)){if(typeof s==="string"&&s in this.event_context){var r=this.event_context[s];s=function(t){return r.apply(this.event_context,[t])}}else{return this}}var q=this;if(this.waiting){this.callbacks.push(s)}else{this.wait();j.setTimeout(function(){var t=s.apply(q,[q.content,q.previous_content]);if(t!==false){q.next(t)}},0)}return this},wait:function(){this.waiting=true},next:function(q){this.waiting=false;if(typeof q!=="undefined"){this.previous_content=this.content;this.content=q}if(this.callbacks.length>0){this.then(this.callbacks.shift())}},load:function(q,r,t){var s=this;return this.then(function(){var u,v,x,w;if(d(r)){t=r;r={}}else{r=h.extend({},r)}if(t){this.then(t)}if(typeof q==="string"){x=(q.match(/\.json$/)||r.json);u=((x&&r.cache===true)||r.cache!==false);s.next_engine=s.event_context.engineFor(q);delete r.cache;delete r.json;if(r.engine){s.next_engine=r.engine;delete r.engine}if(u&&(v=this.event_context.app.templateCache(q))){return v}this.wait();h.ajax(h.extend({url:q,data:{},dataType:x?"json":null,type:"get",success:function(y){if(u){s.event_context.app.templateCache(q,y)}s.next(y)}},r));return false}else{if(q.nodeType){return q.innerHTML}if(q.selector){s.next_engine=q.attr("data-engine");if(r.clone===false){return q.remove()[0].innerHTML.toString()}else{return q[0].innerHTML.toString()}}}})},loadPartials:function(q){if(q){this.partials=this.partials||{};for(name in q){this.load(q[name]).then(function(r){this.partials[name]=r})}}return this},render:function(q,s,t,r){if(d(q)&&!s){return this.then(q)}else{return this.loadPartials(r).load(q).interpolate(s,q).then(t)}},partial:function(q,r){return this.render(q,r).swap()},send:function(){var s=this,r=c(arguments),q=r.shift();if(m(r[0])){r=r[0]}return this.then(function(t){r.push(function(u){s.next(u)});s.wait();q.apply(q,r);return false})},collect:function(u,t,q){var s=this;var r=function(){if(d(u)){t=u;u=this.content}var v=[],w=false;h.each(u,function(x,z){var y=t.apply(s,[x,z]);if(y.jquery&&y.length==1){y=y[0];w=true}v.push(y);return y});return w?v:v.join("")};return q?r():this.then(r)},renderEach:function(q,r,s,t){if(m(r)){t=s;s=r;r=null}return this.load(q).then(function(v){var u=this;if(!s){s=m(this.previous_content)?this.previous_content:[]}if(t){h.each(s,function(w,y){var z={},x=this.next_engine||q;r?(z[r]=y):(z=y);t(y,u.event_context.interpolate(v,z,x))})}else{return this.collect(s,function(w,y){var z={},x=this.next_engine||q;r?(z[r]=y):(z=y);return this.event_context.interpolate(v,z,x)},true)}})},interpolate:function(t,s,q){var r=this;return this.then(function(v,u){if(!t&&u){t=u}if(this.next_engine){s=this.next_engine;this.next_engine=false}var w=r.event_context.interpolate(v,t,s,this.partials);return q?u+w:w})},swap:function(){return this.then(function(q){this.event_context.swap(q)}).trigger("changed",{})},appendTo:function(q){return this.then(function(r){h(q).append(r)}).trigger("changed",{})},prependTo:function(q){return this.then(function(r){h(q).prepend(r)}).trigger("changed",{})},replace:function(q){return this.then(function(r){h(q).html(r)}).trigger("changed",{})},trigger:function(q,r){return this.then(function(s){if(typeof r=="undefined"){r={content:s}}this.event_context.trigger(q,r)})}});p.EventContext=function(u,t,r,s,q){this.app=u;this.verb=t;this.path=r;this.params=new p.Object(s);this.target=q};p.EventContext.prototype=h.extend({},p.Object.prototype,{$element:function(){return this.app.$element(c(arguments).shift())},engineFor:function(s){var r=this,q;if(d(s)){return s}s=(s||r.app.template_engine).toString();if((q=s.match(/\.([^\.\?\#]+)$/))){s=q[1]}if(s&&d(r[s])){return r[s]}if(r.app.template_engine){return this.engineFor(r.app.template_engine)}return function(t,u){return t}},interpolate:function(s,t,r,q){return this.engineFor(r).apply(this,[s,t,q])},render:function(q,s,t,r){return new p.RenderContext(this).render(q,s,t,r)},renderEach:function(q,r,s,t){return new p.RenderContext(this).renderEach(q,r,s,t)},load:function(q,r,s){return new p.RenderContext(this).load(q,r,s)},partial:function(q,r){return new p.RenderContext(this).partial(q,r)},send:function(){var q=new p.RenderContext(this);return q.send.apply(q,arguments)},redirect:function(){var y,w=c(arguments),v=this.app.getLocation(),r=w.length;if(r>1){var u=0,z=[],q=[],t={},x=false;for(;u<r;u++){if(typeof w[u]=="string"){z.push(w[u])}else{h.extend(t,w[u]);x=true}}y=z.join("/");if(x){for(var s in t){q.push(this.app._encodeFormPair(s,t[s]))}y+="?"+q.join("&")}}else{y=w[0]}this.trigger("redirect",{to:y});this.app.last_location=[this.verb,this.path];this.app.setLocation(y);if(new RegExp(y).test(v)){this.app.trigger("location-changed")}},trigger:function(q,r){if(typeof r=="undefined"){r={}}if(!r.context){r.context=this}return this.app.trigger(q,r)},eventNamespace:function(){return this.app.eventNamespace()},swap:function(q){return this.app.swap(q)},notFound:function(){return this.app.notFound(this.verb,this.path)},json:function(q){return h.parseJSON(q)},toString:function(){return"Sammy.EventContext: "+[this.verb,this.path,this.params].join(" ")}});h.sammy=j.Sammy=p})(jQuery,window);
@@ -0,0 +1,5 @@
1
+ // -- Sammy.js -- /plugins/sammy.handlebars.js
2
+ // http://sammyjs.org
3
+ // Version: 0.7.0
4
+ // Built: 2011-07-30 16:55:44 -0700
5
+ (function(a){Sammy=Sammy||{};Sammy.Handlebars=function(e,c){var d={};var b=function(i,j,g,f){if(typeof f=="undefined"){f=i}var h=d[f];if(!h){h=d[f]=Handlebars.compile(i)}j=a.extend({},this,j);g=a.extend({},j.partials,g);return h(j,{partials:g})};if(!c){c="handlebars"}e.helper(c,b)}})(jQuery);
@@ -0,0 +1,5 @@
1
+ // -- Sammy.js -- /plugins/sammy.template.js
2
+ // http://sammyjs.org
3
+ // Version: 0.7.0
4
+ // Built: 2011-07-30 16:55:51 -0700
5
+ (function(c){var a={};var b=function(e,g,h,d){var f,i;if(a[e]){f=a[e]}else{if(typeof g=="undefined"){return false}if(d&&d.escape_html===false){i='",$1,"'}else{i='",h($1),"'}f=a[e]=new Function("obj",'var ___$$$___=[],print=function(){___$$$___.push.apply(___$$$___,arguments);};with(obj){___$$$___.push("'+String(g).replace(/[\r\t\n]/g," ").replace(/\"/g,'\\"').split("<%").join("\t").replace(/((^|%>)[^\t]*)/g,"$1\r").replace(/\t=(.*?)%>/g,i).replace(/\t!(.*?)%>/g,'",$1,"').split("\t").join('");').split("%>").join('___$$$___.push("').split("\r").join("")+"\");}return ___$$$___.join('');")}if(typeof h!="undefined"){return f(h)}else{return f}};Sammy=Sammy||{};Sammy.Template=function(f,d){var e=function(i,j,h,g){if(typeof h=="undefined"){h=i}if(typeof g=="undefined"&&typeof h=="object"){g=h;h=i}return b(h,i,c.extend({},this,j),g)};if(!d){d="template"}f.helper(d,e)}})(jQuery);
@@ -0,0 +1,4 @@
1
+ // left around for reference, not currently used
2
+ tinyMCE.addI18n('en.imageselector',{
3
+ desc : 'This is just a template button'
4
+ });
@@ -0,0 +1,4 @@
1
+ // left around for reference, not currently used
2
+ tinyMCE.addI18n('en.imageselector_dlg',{
3
+ title : 'This is just a example title'
4
+ });
@@ -0,0 +1 @@
1
+ <h3 id=no-result-{{sourceId}} class="no-results-title">No results for {{name}}</h3>
@@ -0,0 +1 @@
1
+ <h3 id="oembed-failed" class="oembed-failed-title">Unable to load oEmbed for {{oembed_url}}</h3>
@@ -0,0 +1,18 @@
1
+ <div id="result-description-and-sizes">
2
+
3
+ <h2 class="result-title">Title: {{result_title}}</h2>
4
+ <h2 class="result-author">by <a href="{{result_author_url}}" target="_blank">{{result_author_name}}</a></h2>
5
+
6
+ <h1 class="result-size-choice-headline">Choose a size</h1>
7
+
8
+ <ul id="result-sizes">
9
+ {{#sizes}}
10
+ <li class="size-choice" id=result-sizes-{{size_name}}>
11
+ <a href=#/selections/{{oembed_url}} class="result-size-choose"><img src="{{url}}" width="{{width}}" height="{{height}}" class="result-size-image"></a>
12
+ <div class="size-name"><a href=#/selections/{{oembed_url}} class="result-size-choose">{{size_name}}</a></div>
13
+ </li>
14
+ {{/sizes}}
15
+ </ul>
16
+
17
+ <div id="home-link"><a href="index.html">Back to Sources</a></div>
18
+ </div>
@@ -0,0 +1,5 @@
1
+ <li class="result-grid">
2
+ <h4 class="result-thumbnail-title">{{title}} [<a href="{{link}}" target="_blank">view original</a>]</h4>
3
+ <a class="result-thumbnail" href=#/results/{{link_escaped}}><img src="{{thumbnail_url}}" class="result-thumbnail"></a>
4
+ <div class="result-thumbnail-select">Select</a></div>
5
+ </li>
@@ -0,0 +1 @@
1
+ <h3 id=results-failed-{{sourceId}} class="results-failed-title">Results from {{name}} failed to load.</h3>
@@ -0,0 +1,14 @@
1
+ <h1 class="selection-headline">Your selection</h1>
2
+
3
+ <h2 class="selection-title">Title: {{title}}</h2>
4
+ <h2 class="selection-author">by <a href="{{author_url}}" target="_blank">{{author_name}}</a></h2>
5
+
6
+ <img src="{{url}}" width="{{width}}" height="{{height}}" class="selection-image">
7
+
8
+ <div id="embed-details">
9
+ <h3>Embed:</h3>
10
+
11
+ <textarea class="selection-embed"><img src="{{url}}" width="{{width}}" height="{{height}}" alt="{{alt}}"> by <a href="{{author_url}}">{{author_name}}</a></textarea>
12
+
13
+ </div>
14
+ <div id="home-link"><a href="index.html">Back to Sources</a></div>
@@ -0,0 +1 @@
1
+ <h3 id="sizes-failed" class="sizes-failed-title">Unable to load sizes settings.</h3>
@@ -0,0 +1 @@
1
+ <li class="source" id={{sourceId}}><a href=#/{{sourceId}}>{{name}}</a></li>
@@ -0,0 +1 @@
1
+ <div id="next-page"><a href=#/{{sourceId}}/page/{{nextPage}}>More results >></a></div>
@@ -0,0 +1,6 @@
1
+ <div id="search-form-next">
2
+ <form action=#/{{sourceId}}/page/{{nextPage}} method="post">
3
+ <input type="hidden" value="{{searchTerms}}" name="search_terms" />
4
+ <input type="submit" value="More results >>" />
5
+ </form>
6
+ </div>
@@ -0,0 +1,9 @@
1
+ <li class="source" id={{sourceId}}>
2
+ <div class="source-search-form">
3
+ <form action="#/{{sourceId}}" method="post">
4
+ <label>{{name}}:</label>
5
+ <input type="text" size="15" name="search_terms" />
6
+ <input type="submit" value="Go" />
7
+ </form>
8
+ </div>
9
+ </li>
@@ -0,0 +1 @@
1
+ <li class="source source-selected" id={{sourceId}}>{{name}}</li>
@@ -0,0 +1,9 @@
1
+ <li class="source source-selected" id={{sourceId}}>
2
+ <div class="source-search-form">
3
+ <form action="#/{{sourceId}}" method="post">
4
+ <label>{{name}}:</label>
5
+ <input type="text" size="15" name="search_terms" value="{{value}}" />
6
+ <input type="submit" value="Update" />
7
+ </form>
8
+ </div>
9
+ </li>
@@ -0,0 +1 @@
1
+ <li class="upload-link"><a href="{{url}}{{service_target_param}}">{{label}}</li>
@@ -0,0 +1,23 @@
1
+ # Make sure the tiny_mce gem has been loaded before we declare any plugins
2
+ require 'tiny_mce'
3
+
4
+ # We need to make sure that the TinyMCE Editor sources are already in place
5
+ # or any changes we make may be overwritten later, this will return if TinyMCE already initialized
6
+ TinyMCE.initialize
7
+
8
+ # Create the TinyMCE Imageselector Plugin. Inherit from TinyMCE::Plugin which sets a
9
+ # default self.install method, which installs all files in self.assets_path
10
+ # into the public/javascripts/tiny_mce directory
11
+ class TinyMCEImageselector < TinyMCE::Plugin
12
+
13
+ # This lets the TinyMCE::Plugin classes install method know where to find
14
+ # the assets we need to install. If were overwrite the self.install method
15
+ # here, then we don't need to provide this line
16
+ self.assets_path = File.join(File.dirname(__FILE__), 'assets')
17
+
18
+ end
19
+
20
+ # Finally, tell the TinyMCEImageselector Plugin to install itself. The install method
21
+ # can be overwritten in the class definition above. TinyMCE::Plugin provide a
22
+ # default one that will work fine in this example
23
+ TinyMCEImageselector.install
@@ -0,0 +1,15 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "tiny_mce_plugin_imageselector"
3
+ s.version = "0.0.2"
4
+ s.authors = ["Walter McGinnis"]
5
+ s.email = "walter@katipo.co.nz"
6
+ s.homepage = "http://github.com/kete/tiny_mce_plugin_imageselector"
7
+ s.summary = "A gem to install the imageselector tiny_mce plugin in tandem with the tinymce gem."
8
+ s.description = "This gem uses the tiny_mce gem's plugin system to install the https://github.com/kete/image_selector_tinymce_plugin plugin."
9
+
10
+ s.files = Dir["lib/**/*", "[A-Z]*", "tiny_mce_plugin_imageselector.gemspec"]
11
+ s.require_path = "lib"
12
+
13
+ s.extra_rdoc_files = Dir["*.rdoc"]
14
+ s.rdoc_options = ["--charset=UTF-8", "--exclude=lib/assets"]
15
+ end
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tiny_mce_plugin_imageselector
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 2
10
+ version: 0.0.2
11
+ platform: ruby
12
+ authors:
13
+ - Walter McGinnis
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-09-23 00:00:00 +12:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: This gem uses the tiny_mce gem's plugin system to install the https://github.com/kete/image_selector_tinymce_plugin plugin.
23
+ email: walter@katipo.co.nz
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files:
29
+ - README.rdoc
30
+ files:
31
+ - lib/assets/plugins/imageselector/css/media_selector.css
32
+ - lib/assets/plugins/imageselector/dialog.htm
33
+ - lib/assets/plugins/imageselector/editor_plugin.js
34
+ - lib/assets/plugins/imageselector/editor_plugin_src.js
35
+ - lib/assets/plugins/imageselector/img/example.gif
36
+ - lib/assets/plugins/imageselector/img/imageselector.png
37
+ - lib/assets/plugins/imageselector/img/spinner.gif
38
+ - lib/assets/plugins/imageselector/js/media_selector.js
39
+ - lib/assets/plugins/imageselector/js/sammy-0.7.0.min.js
40
+ - lib/assets/plugins/imageselector/js/sammy.handlebars-0.7.0.min.js
41
+ - lib/assets/plugins/imageselector/js/sammy.template-0.7.0.min.js
42
+ - lib/assets/plugins/imageselector/langs/en.js
43
+ - lib/assets/plugins/imageselector/langs/en_dlg.js
44
+ - lib/assets/plugins/imageselector/LICENSE
45
+ - lib/assets/plugins/imageselector/README.md
46
+ - lib/assets/plugins/imageselector/templates/no_results.hb
47
+ - lib/assets/plugins/imageselector/templates/oembed_failed.rb
48
+ - lib/assets/plugins/imageselector/templates/result.hb
49
+ - lib/assets/plugins/imageselector/templates/result_thumbnail.hb
50
+ - lib/assets/plugins/imageselector/templates/results_failed.hb
51
+ - lib/assets/plugins/imageselector/templates/selection.hb
52
+ - lib/assets/plugins/imageselector/templates/sizes_failed.rb
53
+ - lib/assets/plugins/imageselector/templates/source.hb
54
+ - lib/assets/plugins/imageselector/templates/source_next_page_link.hb
55
+ - lib/assets/plugins/imageselector/templates/source_next_page_link_search.hb
56
+ - lib/assets/plugins/imageselector/templates/source_search.hb
57
+ - lib/assets/plugins/imageselector/templates/source_selected.hb
58
+ - lib/assets/plugins/imageselector/templates/source_selected_search.hb
59
+ - lib/assets/plugins/imageselector/templates/upload.hb
60
+ - lib/tiny_mce_plugin_imageselector.rb
61
+ - LICENSE
62
+ - README.rdoc
63
+ - tiny_mce_plugin_imageselector.gemspec
64
+ has_rdoc: true
65
+ homepage: http://github.com/kete/tiny_mce_plugin_imageselector
66
+ licenses: []
67
+
68
+ post_install_message:
69
+ rdoc_options:
70
+ - --charset=UTF-8
71
+ - --exclude=lib/assets
72
+ require_paths:
73
+ - lib
74
+ required_ruby_version: !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ hash: 3
80
+ segments:
81
+ - 0
82
+ version: "0"
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ hash: 3
89
+ segments:
90
+ - 0
91
+ version: "0"
92
+ requirements: []
93
+
94
+ rubyforge_project:
95
+ rubygems_version: 1.3.7
96
+ signing_key:
97
+ specification_version: 3
98
+ summary: A gem to install the imageselector tiny_mce plugin in tandem with the tinymce gem.
99
+ test_files: []
100
+