├── .gitignore ├── README.textile ├── application.rb ├── config.ru ├── environment.rb ├── public ├── images │ ├── alert-overlay.png │ ├── background-id.png │ ├── giffer.gif │ ├── icon-pause.png │ ├── icon-play.png │ ├── icon-zoom-minus.png │ └── icon-zoom-plus.png ├── js │ ├── giffer.js │ ├── jquery-1.4.1.min.js │ └── jquery-ui.js ├── robots.txt └── style.css └── views ├── index.haml └── style.sass /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store -------------------------------------------------------------------------------- /README.textile: -------------------------------------------------------------------------------- 1 | h1. Kathy Lee Giffer 2 | 3 | HTML5 drag and drop animated GIF creation! Uses Gifsicle and Ruby to create the GIFs on the server 4 | 5 | !http://i.imgur.com/o6KbD.png! 6 | 7 | h2. Requirements 8 | * rubygems: sinatra, pow, haml and their dependencies 9 | * @convert@ (installed with the ImageMagick package) 10 | * @gifsicle@ ("http://www.lcdf.org/gifsicle/":http://www.lcdf.org/gifsicle/) 11 | * modern web browser with the FileReader API (tested against recent firefox, chrome, and safari builds) 12 | 13 | h2. Installation (brew for OS X, for Ubuntu replace brew with apt-get) 14 | 15 |
16 |
17 | brew install imagemagick
18 | brew install gifsicle
19 | gem install sinatra pow haml unicorn
20 | mkdir tmp
21 | mkdir log
22 | mkdir public/images/gifs
23 | unicorn config.ru
24 |
25 |
26 |
27 | h1. Contributors
28 |
29 | codez by Max Ogden
30 | perdy styles by Effalo Corp
31 |
32 | h1. License
33 |
34 | (The MIT License)
35 |
36 | Copyright (c) 2010 Max Ogden
37 |
38 | Permission is hereby granted, free of charge, to any person obtaining
39 | a copy of this software and associated documentation files (the
40 | 'Software'), to deal in the Software without restriction, including
41 | without limitation the rights to use, copy, modify, merge, publish,
42 | distribute, sublicense, and/or sell copies of the Software, and to
43 | permit persons to whom the Software is furnished to do so, subject to
44 | the following conditions:
45 |
46 | The above copyright notice and this permission notice shall be
47 | included in all copies or substantial portions of the Software.
48 |
49 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
50 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
51 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
52 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
53 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
54 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
55 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
56 |
--------------------------------------------------------------------------------
/application.rb:
--------------------------------------------------------------------------------
1 | get '/' do
2 | haml :index
3 | end
4 |
5 | post '/gifit' do
6 | pwd = File.join(Dir.pwd, 'tmp')
7 | params[:upload].each_with_index do |upload, i|
8 | filetype = upload[1]['content_type'].split('/')[1].downcase
9 | File.open(File.join(pwd, "#{i}.#{filetype}"), 'w') {|f| f.write(Base64.decode64(upload[1]['data'])) }
10 | end
11 | images = Dir.glob(File.join(pwd, "*.{jpg,jpeg,png}"))
12 | images.each do |image|
13 | filename = File.basename(image).split('.')[0]
14 | output = "#{filename}.gif"
15 | `convert #{File.expand_path(image)} #{File.join(pwd, output)}`
16 | `rm #{File.expand_path(image)}`
17 | end
18 | gifname = "#{Time.now.to_i}.gif"
19 | `gifsicle --delay=10 --loop #{pwd}/*.gif > #{File.join(Dir.pwd, "public", "images", "gifs", gifname)}`
20 | `rm #{pwd}/*`
21 | "/images/gifs/#{gifname}"
22 | end
--------------------------------------------------------------------------------
/config.ru:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 | require 'logger'
3 | require 'rack'
4 | require 'sinatra'
5 | require 'pow'
6 | require 'haml'
7 | require 'base64'
8 | require 'sass/plugin/rack'
9 | require 'environment'
10 | require 'application'
11 |
12 | use Sass::Plugin::Rack
13 | Sass::Plugin.options[:css_location] = "./public"
14 | Sass::Plugin.options[:template_location] = "./views"
15 |
16 | log = File.new("log/sinatra.log", "a+")
17 | STDOUT.reopen(log)
18 | STDERR.reopen(log)
19 |
20 | run Sinatra::Application
--------------------------------------------------------------------------------
/environment.rb:
--------------------------------------------------------------------------------
1 | configure do
2 | set :views, "#{File.dirname(__FILE__)}/views"
3 | LOGGER = Logger.new("log/sinatra.log")
4 | end
5 |
6 | helpers do
7 | def logger
8 | LOGGER
9 | end
10 |
11 | def base_dir
12 | "public/media"
13 | end
14 |
15 | def partial(page, options={})
16 | haml page, options.merge!(:layout => false)
17 | end
18 | end
--------------------------------------------------------------------------------
/public/images/alert-overlay.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/max-mapper/kathyleegiffer/10f15adba9c82b009c6b50a088076287062ea1e5/public/images/alert-overlay.png
--------------------------------------------------------------------------------
/public/images/background-id.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/max-mapper/kathyleegiffer/10f15adba9c82b009c6b50a088076287062ea1e5/public/images/background-id.png
--------------------------------------------------------------------------------
/public/images/giffer.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/max-mapper/kathyleegiffer/10f15adba9c82b009c6b50a088076287062ea1e5/public/images/giffer.gif
--------------------------------------------------------------------------------
/public/images/icon-pause.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/max-mapper/kathyleegiffer/10f15adba9c82b009c6b50a088076287062ea1e5/public/images/icon-pause.png
--------------------------------------------------------------------------------
/public/images/icon-play.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/max-mapper/kathyleegiffer/10f15adba9c82b009c6b50a088076287062ea1e5/public/images/icon-play.png
--------------------------------------------------------------------------------
/public/images/icon-zoom-minus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/max-mapper/kathyleegiffer/10f15adba9c82b009c6b50a088076287062ea1e5/public/images/icon-zoom-minus.png
--------------------------------------------------------------------------------
/public/images/icon-zoom-plus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/max-mapper/kathyleegiffer/10f15adba9c82b009c6b50a088076287062ea1e5/public/images/icon-zoom-plus.png
--------------------------------------------------------------------------------
/public/js/giffer.js:
--------------------------------------------------------------------------------
1 | var attachments = [];
2 |
3 | $(document).ready(function(){
4 |
5 | if (!FileReader) {
6 | $('#drop').html('=0))k||l.push(u);else if(k)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&& 79 | "2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,k,l,q,p){h=g[1].replace(/\\/g,"");if(!p&&m.attrMap[h])g[1]=m.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,k,l,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=o(g[3],null,null,h);else{g=o.filter(g[3],h,k,true^q);k||l.push.apply(l,g);return false}else if(m.match.POS.test(g[0])||m.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true); 80 | return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,k){return!!o(k[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"=== 81 | g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},setFilters:{first:function(g,h){return h===0},last:function(g,h,k,l){return h===l.length-1},even:function(g,h){return h%2=== 82 | 0},odd:function(g,h){return h%2===1},lt:function(g,h,k){return hk[3]-0},nth:function(g,h,k){return k[3]-0===h},eq:function(g,h,k){return k[3]-0===h}},filter:{PSEUDO:function(g,h,k,l){var q=h[1],p=m.filters[q];if(p)return p(g,k,h,l);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h=h[3];k=0;for(l=h.length;k = 84 | 0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var k=h[1];g=m.attrHandle[k]?m.attrHandle[k](g):g[k]!=null?g[k]:g.getAttribute(k);k=g+"";var l=h[2];h=h[4];return g==null?l==="!=":l==="="?k===h:l==="*="?k.indexOf(h)>=0:l==="~="?(" "+k+" ").indexOf(h)>=0:!h?k&&g!==false:l==="!="?k!==h:l==="^="? 85 | k.indexOf(h)===0:l==="$="?k.substr(k.length-h.length)===h:l==="|="?k===h||k.substr(0,h.length+1)===h+"-":false},POS:function(g,h,k,l){var q=m.setFilters[h[2]];if(q)return q(g,k,h,l)}}},s=m.match.POS;for(var x in m.match){m.match[x]=new RegExp(m.match[x].source+/(?![^\[]*\])(?![^\(]*\))/.source);m.leftMatch[x]=new RegExp(/(^(?:.|\r|\n)*?)/.source+m.match[x].source.replace(/\\(\d+)/g,function(g,h){return"\\"+(h-0+1)}))}var A=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g}; 86 | try{Array.prototype.slice.call(r.documentElement.childNodes,0)}catch(B){A=function(g,h){h=h||[];if(i.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var k=0,l=g.length;k ";var k=r.documentElement;k.insertBefore(g,k.firstChild);if(r.getElementById(h)){m.find.ID=function(l,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(l[1]))?q.id===l[1]||typeof q.getAttributeNode!=="undefined"&&q.getAttributeNode("id").nodeValue===l[1]?[q]:v:[]};m.filter.ID=function(l,q){var p=typeof l.getAttributeNode!=="undefined"&&l.getAttributeNode("id"); 89 | return l.nodeType===1&&p&&p.nodeValue===q}}k.removeChild(g);k=g=null})();(function(){var g=r.createElement("div");g.appendChild(r.createComment(""));if(g.getElementsByTagName("*").length>0)m.find.TAG=function(h,k){k=k.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var l=0;k[l];l++)k[l].nodeType===1&&h.push(k[l]);k=h}return k};g.innerHTML="";if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")m.attrHandle.href=function(h){return h.getAttribute("href", 90 | 2)};g=null})();r.querySelectorAll&&function(){var g=o,h=r.createElement("div");h.innerHTML="";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){o=function(l,q,p,u){q=q||r;if(!u&&q.nodeType===9&&!w(q))try{return A(q.querySelectorAll(l),p)}catch(t){}return g(l,q,p,u)};for(var k in g)o[k]=g[k];h=null}}();(function(){var g=r.createElement("div");g.innerHTML="";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length=== 91 | 0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){m.order.splice(1,0,"CLASS");m.find.CLASS=function(h,k,l){if(typeof k.getElementsByClassName!=="undefined"&&!l)return k.getElementsByClassName(h[1])};g=null}}})();var E=r.compareDocumentPosition?function(g,h){return g.compareDocumentPosition(h)&16}:function(g,h){return g!==h&&(g.contains?g.contains(h):true)},w=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},fa=function(g,h){var k=[], 92 | l="",q;for(h=h.nodeType?[h]:h;q=m.match.PSEUDO.exec(g);){l+=q[0];g=g.replace(m.match.PSEUDO,"")}g=m.relative[g]?g+"*":g;q=0;for(var p=h.length;q =0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f
0)for(var i=d;i 0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,i={},j;if(f&&a.length){e=0;for(var n=a.length;e 95 | -1:c(f).is(e)){d.push({selector:j,elem:f});delete i[j]}}f=f.parentNode}}return d}var o=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(m,s){for(;s&&s.ownerDocument&&s!==b;){if(o?o.index(s)>-1:c(s).is(a))return s;s=s.parentNode}return null})},index:function(a){if(!a||typeof a==="string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(), 96 | a);return this.pushStack(pa(a[0])||pa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")}, 97 | nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);bb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e): 98 | e;if((this.length>1||db.test(f))&&cb.test(a))e=e.reverse();return this.pushStack(e,a,Q.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===v||a.nodeType!==1||!c(a).is(d));){a.nodeType===1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!== 99 | b&&d.push(a);return d}});var Fa=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ga=/(<([\w:]+)[^>]*?)\/>/g,eb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,Ha=/<([\w:]+)/,fb=/"+d+">"},F={option:[1,""],legend:[1,""],thead:[1," ","
"],tr:[2,"","
"],td:[3,""], 100 | col:[2,"
"," "],area:[1,""],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div
"," ",""];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==v)return this.empty().append((this[0]&&this[0].ownerDocument||r).createTextNode(a));return c.getText(this)}, 101 | wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length? 102 | d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments, 103 | false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&& 104 | !c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Fa,"").replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){qa(this,b);qa(this.find("*"),b.find("*"))}return b},html:function(a){if(a===v)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Fa,""):null;else if(typeof a==="string"&&!/