Initial check-in after being scrubbed of sensitive data.

This commit is contained in:
Elf M. Sternberg 2012-03-27 16:18:55 -07:00
commit 659b11b929
43 changed files with 3093 additions and 0 deletions

13
.gitignore vendored Normal file
View File

@ -0,0 +1,13 @@
*.pyc
*.pyo
*#
.#*
*~
js/magnets.js
js/sat.js
js/wordlist.js
node_modules
server/magnet_server.js
index.html
style.css
private/

19
LICENSE Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2012 Elf M. Sternberg
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

34
Makefile Normal file
View File

@ -0,0 +1,34 @@
.PHONY: watch
HAML=/usr/bin/haml
COFFEE = coffee
LESSCSS=lessc
SED=sed
COMPILER=uglifyjs
INCLUDES= js/jquery-1.6.2.min.js js/underscore.js js/backbone.js js/jquery-ui-1.8.16.custom.min.js \
js/jquery-css-transform.js js/jquery-animate-css-rotate-scale.js js/buzz.js
all: index.html style.css js/magnets.js js/sat.js js/wordlist.js
js/magnets.js: src/magnets.coffee
$(COFFEE) --compile --lint --output js/ $<
js/wordlist.js: src/wordlist.coffee
$(COFFEE) --compile --lint --bare --output js/ $<
$(SED) -i -e '$$ s/;$$//' $@
js/sat.js: src/sat.coffee
$(COFFEE) --compile --lint --output js/ $<
style.css: src/style.less
$(LESSCSS) $< $@
index.html: src/index.haml
$(HAML) --unix-newlines --no-escape-attrs --double-quote-attributes $< > $@
compile: all
cat ${INCLUDES} js/magnets.js | ${COMPILER} > js/compiled.js
watch:
while inotifywait src/*.less src/*.haml src/*.coffee ; do make all; fab send_client ; done

BIN
PikePlaceMarket.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

101
README Normal file
View File

@ -0,0 +1,101 @@
# Fridgemagnets - A Nifty HTML5 Toy With Some Twitterable Features!
## Demo
Main website: [HTML5 Magnets!](http://html5magnets.elfsternberg.com)
Happy results: [@HTML5Magnets](https://twitter.com/#!/html5magnets)
## Main Idea
Fridgemagnets is a straightforward simulation of a relaxing
refrigerator poem tileset. It was inspired by
[TwitterMagnets](http://twittermagnets.com/), a Flash app written by the
brilliant graphic designers at
[PlusGood](http://www.plusgood.co.uk/). I have a bit of Flash envy,
since I'm not an Adobe developer, and the TwitterMagnets application
bugged me. It didn't resize, it didn't do mobile very well, and
nobody has a space reserved on their fridge for the poem: a "poem" is
just a meaningful arrangement of words deliberately placed in close
proximity that seems to convey meaning.
Fridgemagnets has a lot of new and fun technologies: it uses the audio
API, it involves all manner of write-only-DOM tricks to make resizing
work well, and it's my first major piece of express.js software. (I
originally thought of using Zappa, but decided against it; dispatch is
not the biggest thing Node has to deal with, and express by itself
works just fine in Coffee.)
I can now add the Twitter API, the HTML5 Audio API, and some basic
game mechanics ([Separate Axis
Theorem](http://www.metanetsoftware.com/technique/tutorialA.html) for
collision management, anyone?) to my resume.
This is known to work in later versions of Chrome, Firefox, and IE8+
under Windows XP. No promise is implied of it working on your version
of those, or any other browser. It's not (yet) phone-ready.
## Requirements
Node.js. Most of the subsidiary requirements can be found in the two
package.json files. For development purposes Coffeescript, LessCSS,
and HAML are in heavy use.
If you're running the server, you need MySQL. The schema for the
MySQL database can be found in the server folder.
A config file. There's an example in the server folder.
A twitter developer's account. Get one at dev.twitter.com.
If you're going to be using the test/deploy routine, inotify-tools and
python's "fabric" program are very useful.
If you're going to make this publicly available, I strongly recommend
you run this as its own user in a low-permissions container, behind
Nginx and a lot of smarts. Also, the Node.js program "forever" is
very useful in keeping the server up.
## Acknowledgements
[PlusGood](http://www.plusgood.co.uk/), for the inspiration.
[Emily Richards aka Snowflake](http://ccmixter.org/people/snowflake),
for her beautiful music.
The entire crew at [Nodejitsu](http://nodejitsu.com/), for all the
encouragement, even if I don't use their services.
## CREDITS
"Ethereal Space" is copyright (c) 2011 Snowflake, licensed under a
Creative Commons 3.0 Attribution-Required license.
jQuery, jQuery UI and associated assets, Buzz.js, and jQuery CSS
Transform are copyright their respective owners, and available under
a permissive MIT license.
## LICENSE AND COPYRIGHT NOTICE: NO WARRANTY GRANTED OR IMPLIED
Copyright (c) 2012 Elf M. Sternberg
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
- Elf M. Sternberg <elf@pendorwright.com>

BIN
alphamod.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 B

24
fabfile.py vendored Normal file
View File

@ -0,0 +1,24 @@
from fabric.api import *
from fabric.decorators import hosts
import fabric.contrib
# Devserver
# env.user = 'ubuntu'
# DEFAULT_HOSTS = [env.user + 'devserver']
# Production Server
env.user = 'htmlmagnets'
DEFAULT_HOSTS = [env.user + '@elfsternberg.com']
def send_server():
rsync_cmd = 'rsync -r --progress'
local('%s %s %s:%s' % (rsync_cmd, "server/magnet_server.coffee js/wordlist.js private/config.coffee", DEFAULT_HOSTS[0], "server/"))
def send_client():
rsync_cmd = 'rsync -r --progress'
content = 'index.html *.jpg *.png *.css js media ui-lightness'
local('%s %s %s:%s' % (rsync_cmd, content, DEFAULT_HOSTS[0], "htdocs/"))

854
js/buzz.js Normal file
View File

@ -0,0 +1,854 @@
// ----------------------------------------------------------------------------
// Buzz, a Javascript HTML5 Audio library
// v 1.0.4 beta
// Licensed under the MIT license.
// http://buzz.jaysalvat.com/
// ----------------------------------------------------------------------------
// Copyright (C) 2011 Jay Salvat
// http://jaysalvat.com/
// ----------------------------------------------------------------------------
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files ( the "Software" ), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// ----------------------------------------------------------------------------
var buzz = {
defaults: {
autoplay: false,
duration: 5000,
formats: [],
loop: false,
placeholder: '--',
preload: 'metadata',
volume: 80
},
types: {
'mp3': 'audio/mpeg',
'ogg': 'audio/ogg',
'wav': 'audio/wav',
'aac': 'audio/aac',
'm4a': 'audio/x-m4a'
},
sounds: [],
el: document.createElement( 'audio' ),
sound: function( src, options ) {
options = options || {};
var pid = 0,
events = [],
eventsOnce = {},
supported = buzz.isSupported();
// publics
this.load = function() {
if ( !supported ) {
return this;
}
this.sound.load();
return this;
};
this.play = function() {
if ( !supported ) {
return this;
}
this.sound.play();
return this;
};
this.togglePlay = function() {
if ( !supported ) {
return this;
}
if ( this.sound.paused ) {
this.sound.play();
} else {
this.sound.pause();
}
return this;
};
this.pause = function() {
if ( !supported ) {
return this;
}
this.sound.pause();
return this;
};
this.isPaused = function() {
if ( !supported ) {
return null;
}
return this.sound.paused;
};
this.stop = function() {
if ( !supported ) {
return this;
}
this.setTime( this.getDuration() );
this.sound.pause();
return this;
};
this.isEnded = function() {
if ( !supported ) {
return null;
}
return this.sound.ended;
};
this.loop = function() {
if ( !supported ) {
return this;
}
this.sound.loop = 'loop';
this.bind( 'ended.buzzloop', function() {
this.currentTime = 0;
this.play();
});
return this;
};
this.unloop = function() {
if ( !supported ) {
return this;
}
this.sound.removeAttribute( 'loop' );
this.unbind( 'ended.buzzloop' );
return this;
};
this.mute = function() {
if ( !supported ) {
return this;
}
this.sound.muted = true;
return this;
};
this.unmute = function() {
if ( !supported ) {
return this;
}
this.sound.muted = false;
return this;
};
this.toggleMute = function() {
if ( !supported ) {
return this;
}
this.sound.muted = !this.sound.muted;
return this;
};
this.isMuted = function() {
if ( !supported ) {
return null;
}
return this.sound.muted;
};
this.setVolume = function( volume ) {
if ( !supported ) {
return this;
}
if ( volume < 0 ) {
volume = 0;
}
if ( volume > 100 ) {
volume = 100;
}
this.volume = volume;
this.sound.volume = volume / 100;
return this;
};
this.getVolume = function() {
if ( !supported ) {
return this;
}
return this.volume;
};
this.increaseVolume = function( value ) {
return this.setVolume( this.volume + ( value || 1 ) );
};
this.decreaseVolume = function( value ) {
return this.setVolume( this.volume - ( value || 1 ) );
};
this.setTime = function( time ) {
if ( !supported ) {
return this;
}
this.whenReady( function() {
this.sound.currentTime = time;
});
return this;
};
this.getTime = function() {
if ( !supported ) {
return null;
}
var time = Math.round( this.sound.currentTime * 100 ) / 100;
return isNaN( time ) ? buzz.defaults.placeholder : time;
};
this.setPercent = function( percent ) {
if ( !supported ) {
return this;
}
return this.setTime( buzz.fromPercent( percent, this.sound.duration ) );
};
this.getPercent = function() {
if ( !supported ) {
return null;
}
var percent = Math.round( buzz.toPercent( this.sound.currentTime, this.sound.duration ) );
return isNaN( percent ) ? buzz.defaults.placeholder : percent;
};
this.setSpeed = function( duration ) {
if ( !supported ) {
return this;
}
this.sound.playbackRate = duration;
};
this.getSpeed = function() {
if ( !supported ) {
return null;
}
return this.sound.playbackRate;
};
this.getDuration = function() {
if ( !supported ) {
return null;
}
var duration = Math.round( this.sound.duration * 100 ) / 100;
return isNaN( duration ) ? buzz.defaults.placeholder : duration;
};
this.getPlayed = function() {
if ( !supported ) {
return null;
}
return timerangeToArray( this.sound.played );
};
this.getBuffered = function() {
if ( !supported ) {
return null;
}
return timerangeToArray( this.sound.buffered );
};
this.getSeekable = function() {
if ( !supported ) {
return null;
}
return timerangeToArray( this.sound.seekable );
};
this.getErrorCode = function() {
if ( supported && this.sound.error ) {
return this.sound.error.code;
}
return 0;
};
this.getErrorMessage = function() {
if ( !supported ) {
return null;
}
switch( this.getErrorCode() ) {
case 1:
return 'MEDIA_ERR_ABORTED';
case 2:
return 'MEDIA_ERR_NETWORK';
case 3:
return 'MEDIA_ERR_DECODE';
case 4:
return 'MEDIA_ERR_SRC_NOT_SUPPORTED';
default:
return null;
}
};
this.getStateCode = function() {
if ( !supported ) {
return null;
}
return this.sound.readyState;
};
this.getStateMessage = function() {
if ( !supported ) {
return null;
}
switch( this.getStateCode() ) {
case 0:
return 'HAVE_NOTHING';
case 1:
return 'HAVE_METADATA';
case 2:
return 'HAVE_CURRENT_DATA';
case 3:
return 'HAVE_FUTURE_DATA';
case 4:
return 'HAVE_ENOUGH_DATA';
default:
return null;
}
};
this.getNetworkStateCode = function() {
if ( !supported ) {
return null;
}
return this.sound.networkState;
};
this.getNetworkStateMessage = function() {
if ( !supported ) {
return null;
}
switch( this.getNetworkStateCode() ) {
case 0:
return 'NETWORK_EMPTY';
case 1:
return 'NETWORK_IDLE';
case 2:
return 'NETWORK_LOADING';
case 3:
return 'NETWORK_NO_SOURCE';
default:
return null;
}
};
this.set = function( key, value ) {
if ( !supported ) {
return this;
}
this.sound[ key ] = value;
return this;
};
this.get = function( key ) {
if ( !supported ) {
return null;
}
return key ? this.sound[ key ] : this.sound;
};
this.bind = function( types, func ) {
if ( !supported ) {
return this;
}
types = types.split( ' ' );
var that = this,
efunc = function( e ) { func.call( that, e ); };
for( var t = 0; t < types.length; t++ ) {
var type = types[ t ],
idx = type;
type = idx.split( '.' )[ 0 ];
events.push( { idx: idx, func: efunc } );
this.sound.addEventListener( type, efunc, true );
}
return this;
};
this.unbind = function( types ) {
if ( !supported ) {
return this;
}
types = types.split( ' ' );
for( var t = 0; t < types.length; t++ ) {
var idx = types[ t ],
type = idx.split( '.' )[ 0 ];
for( var i = 0; i < events.length; i++ ) {
var namespace = events[ i ].idx.split( '.' );
if ( events[ i ].idx == idx || ( namespace[ 1 ] && namespace[ 1 ] == idx.replace( '.', '' ) ) ) {
this.sound.removeEventListener( type, events[ i ].func, true );
delete events[ i ];
}
}
}
return this;
};
this.bindOnce = function( type, func ) {
if ( !supported ) {
return this;
}
var that = this;
eventsOnce[ pid++ ] = false;
this.bind( pid + type, function() {
if ( !eventsOnce[ pid ] ) {
eventsOnce[ pid ] = true;
func.call( that );
}
that.unbind( pid + type );
});
};
this.trigger = function( types ) {
if ( !supported ) {
return this;
}
types = types.split( ' ' );
for( var t = 0; t < types.length; t++ ) {
var idx = types[ t ];
for( var i = 0; i < events.length; i++ ) {
var eventType = events[ i ].idx.split( '.' );
if ( events[ i ].idx == idx || ( eventType[ 0 ] && eventType[ 0 ] == idx.replace( '.', '' ) ) ) {
var evt = document.createEvent('HTMLEvents');
evt.initEvent( eventType[ 0 ], false, true );
this.sound.dispatchEvent( evt );
}
}
}
return this;
};
this.fadeTo = function( to, duration, callback ) {
if ( !supported ) {
return this;
}
if ( duration instanceof Function ) {
callback = duration;
duration = buzz.defaults.duration;
} else {
duration = duration || buzz.defaults.duration;
}
var from = this.volume,
delay = duration / Math.abs( from - to ),
that = this;
this.play();
function doFade() {
setTimeout( function() {
if ( from < to && that.volume < to ) {
that.setVolume( that.volume += 1 );
doFade();
} else if ( from > to && that.volume > to ) {
that.setVolume( that.volume -= 1 );
doFade();
} else if ( callback instanceof Function ) {
callback.apply( that );
}
}, delay );
}
this.whenReady( function() {
doFade();
});
return this;
};
this.fadeIn = function( duration, callback ) {
if ( !supported ) {
return this;
}
return this.setVolume(0).fadeTo( 100, duration, callback );
};
this.fadeOut = function( duration, callback ) {
if ( !supported ) {
return this;
}
return this.fadeTo( 0, duration, callback );
};
this.fadeWith = function( sound, duration ) {
if ( !supported ) {
return this;
}
this.fadeOut( duration, function() {
this.stop();
});
sound.play().fadeIn( duration );
return this;
};
this.whenReady = function( func ) {
if ( !supported ) {
return null;
}
var that = this;
if ( this.sound.readyState === 0 ) {
this.bind( 'canplay.buzzwhenready', function() {
func.call( that );
});
} else {
func.call( that );
}
};
// privates
function timerangeToArray( timeRange ) {
var array = [],
length = timeRange.length - 1;
for( var i = 0; i <= length; i++ ) {
array.push({
start: timeRange.start( length ),
end: timeRange.end( length )
});
}
return array;
}
function getExt( filename ) {
return filename.split('.').pop();
}
function addSource( sound, src ) {
var source = document.createElement( 'source' );
source.src = src;
if ( buzz.types[ getExt( src ) ] ) {
source.type = buzz.types[ getExt( src ) ];
}
sound.appendChild( source );
}
// init
if ( supported ) {
for(var i in buzz.defaults ) {
if(buzz.defaults.hasOwnProperty(i)) {
options[ i ] = options[ i ] || buzz.defaults[ i ];
}
}
this.sound = document.createElement( 'audio' );
if ( src instanceof Array ) {
for( var j in src ) {
if(src.hasOwnProperty(j)) {
addSource( this.sound, src[ j ] );
}
}
} else if ( options.formats.length ) {
for( var k in options.formats ) {
if(options.formats.hasOwnProperty(k)) {
addSource( this.sound, src + '.' + options.formats[ k ] );
}
}
} else {
addSource( this.sound, src );
}
if ( options.loop ) {
this.loop();
}
if ( options.autoplay ) {
this.sound.autoplay = 'autoplay';
}
if ( options.preload === true ) {
this.sound.preload = 'auto';
} else if ( options.preload === false ) {
this.sound.preload = 'none';
} else {
this.sound.preload = options.preload;
}
this.setVolume( options.volume );
buzz.sounds.push( this );
}
},
group: function( sounds ) {
sounds = argsToArray( sounds, arguments );
// publics
this.getSounds = function() {
return sounds;
};
this.add = function( soundArray ) {
soundArray = argsToArray( soundArray, arguments );
for( var a = 0; a < soundArray.length; a++ ) {
sounds.push( soundArray[ a ] );
}
};
this.remove = function( soundArray ) {
soundArray = argsToArray( soundArray, arguments );
for( var a = 0; a < soundArray.length; a++ ) {
for( var i = 0; i < sounds.length; i++ ) {
if ( sounds[ i ] == soundArray[ a ] ) {
delete sounds[ i ];
break;
}
}
}
};
this.load = function() {
fn( 'load' );
return this;
};
this.play = function() {
fn( 'play' );
return this;
};
this.togglePlay = function( ) {
fn( 'togglePlay' );
return this;
};
this.pause = function( time ) {
fn( 'pause', time );
return this;
};
this.stop = function() {
fn( 'stop' );
return this;
};
this.mute = function() {
fn( 'mute' );
return this;
};
this.unmute = function() {
fn( 'unmute' );
return this;
};
this.toggleMute = function() {
fn( 'toggleMute' );
return this;
};
this.setVolume = function( volume ) {
fn( 'setVolume', volume );
return this;
};
this.increaseVolume = function( value ) {
fn( 'increaseVolume', value );
return this;
};
this.decreaseVolume = function( value ) {
fn( 'decreaseVolume', value );
return this;
};
this.loop = function() {
fn( 'loop' );
return this;
};
this.unloop = function() {
fn( 'unloop' );
return this;
};
this.setTime = function( time ) {
fn( 'setTime', time );
return this;
};
this.setduration = function( duration ) {
fn( 'setduration', duration );
return this;
};
this.set = function( key, value ) {
fn( 'set', key, value );
return this;
};
this.bind = function( type, func ) {
fn( 'bind', type, func );
return this;
};
this.unbind = function( type ) {
fn( 'unbind', type );
return this;
};
this.bindOnce = function( type, func ) {
fn( 'bindOnce', type, func );
return this;
};
this.trigger = function( type ) {
fn( 'trigger', type );
return this;
};
this.fade = function( from, to, duration, callback ) {
fn( 'fade', from, to, duration, callback );
return this;
};
this.fadeIn = function( duration, callback ) {
fn( 'fadeIn', duration, callback );
return this;
};
this.fadeOut = function( duration, callback ) {
fn( 'fadeOut', duration, callback );
return this;
};
// privates
function fn() {
var args = argsToArray( null, arguments ),
func = args.shift();
for( var i = 0; i < sounds.length; i++ ) {
sounds[ i ][ func ].apply( sounds[ i ], args );
}
}
function argsToArray( array, args ) {
return ( array instanceof Array ) ? array : Array.prototype.slice.call( args );
}
},
all: function() {
return new buzz.group( buzz.sounds );
},
isSupported: function() {
return !!buzz.el.canPlayType;
},
isOGGSupported: function() {
return !!buzz.el.canPlayType && buzz.el.canPlayType( 'audio/ogg; codecs="vorbis"' );
},
isWAVSupported: function() {
return !!buzz.el.canPlayType && buzz.el.canPlayType( 'audio/wav; codecs="1"' );
},
isMP3Supported: function() {
return !!buzz.el.canPlayType && buzz.el.canPlayType( 'audio/mpeg;' );
},
isAACSupported: function() {
return !!buzz.el.canPlayType && ( buzz.el.canPlayType( 'audio/x-m4a;' ) || buzz.el.canPlayType( 'audio/aac;' ) );
},
toTimer: function( time, withHours ) {
var h, m, s;
h = Math.floor( time / 3600 );
h = isNaN( h ) ? '--' : ( h >= 10 ) ? h : '0' + h;
m = withHours ? Math.floor( time / 60 % 60 ) : Math.floor( time / 60 );
m = isNaN( m ) ? '--' : ( m >= 10 ) ? m : '0' + m;
s = Math.floor( time % 60 );
s = isNaN( s ) ? '--' : ( s >= 10 ) ? s : '0' + s;
return withHours ? h + ':' + m + ':' + s : m + ':' + s;
},
fromTimer: function( time ) {
var splits = time.toString().split( ':' );
if ( splits && splits.length == 3 ) {
time = ( parseInt( splits[ 0 ], 10 ) * 3600 ) + ( parseInt(splits[ 1 ], 10 ) * 60 ) + parseInt( splits[ 2 ], 10 );
}
if ( splits && splits.length == 2 ) {
time = ( parseInt( splits[ 0 ], 10 ) * 60 ) + parseInt( splits[ 1 ], 10 );
}
return time;
},
toPercent: function( value, total, decimal ) {
var r = Math.pow( 10, decimal || 0 );
return Math.round( ( ( value * 100 ) / total ) * r ) / r;
},
fromPercent: function( percent, total, decimal ) {
var r = Math.pow( 10, decimal || 0 );
return Math.round( ( ( total / 100 ) * percent ) * r ) / r;
}
};

4
js/jquery-1.7.1.min.js vendored Normal file

File diff suppressed because one or more lines are too long

135
js/jquery-animate-css-rotate-scale.js vendored Normal file
View File

@ -0,0 +1,135 @@
(function ($) {
// Monkey patch jQuery 1.3.1+ to add support for setting or animating CSS
// scale and rotation independently.
// 2009-2010 Zachary Johnson www.zachstronaut.com
// Updated 2010.11.06
var rotateUnits = 'deg';
$.fn.rotate = function (val)
{
var style = $(this).css('transform') || 'none';
if (typeof val == 'undefined')
{
if (style)
{
var m = style.match(/rotate\(([^)]+)\)/);
if (m && m[1])
{
return m[1];
}
}
return 0;
}
var m = val.toString().match(/^(-?\d+(\.\d+)?)(.+)?$/);
if (m)
{
if (m[3])
{
rotateUnits = m[3];
}
$(this).css(
'transform',
style.replace(/none|rotate\([^)]*\)/, '') + 'rotate(' + m[1] + rotateUnits + ')'
);
}
return this;
}
// Note that scale is unitless.
$.fn.scale = function (val, duration, options)
{
var style = $(this).css('transform');
if (typeof val == 'undefined')
{
if (style)
{
var m = style.match(/scale\(([^)]+)\)/);
if (m && m[1])
{
return m[1];
}
}
return 1;
}
$(this).css(
'transform',
style.replace(/none|scale\([^)]*\)/, '') + 'scale(' + val + ')'
);
return this;
}
// fx.cur() must be monkey patched because otherwise it would always
// return 0 for current rotate and scale values
var curProxied = $.fx.prototype.cur;
$.fx.prototype.cur = function ()
{
if (this.prop == 'rotate')
{
return parseFloat($(this.elem).rotate());
}
else if (this.prop == 'scale')
{
return parseFloat($(this.elem).scale());
}
return curProxied.apply(this, arguments);
}
$.fx.step.rotate = function (fx)
{
$(fx.elem).rotate(fx.now + rotateUnits);
}
$.fx.step.scale = function (fx)
{
$(fx.elem).scale(fx.now);
}
/*
Starting on line 3905 of jquery-1.3.2.js we have this code:
// We need to compute starting value
if ( unit != "px" ) {
self.style[ name ] = (end || 1) + unit;
start = ((end || 1) / e.cur(true)) * start;
self.style[ name ] = start + unit;
}
This creates a problem where we cannot give units to our custom animation
because if we do then this code will execute and because self.style[name]
does not exist where name is our custom animation's name then e.cur(true)
will likely return zero and create a divide by zero bug which will set
start to NaN.
The following monkey patch for animate() gets around this by storing the
units used in the rotation definition and then stripping the units off.
*/
var animateProxied = $.fn.animate;
$.fn.animate = function (prop)
{
if (typeof prop['rotate'] != 'undefined')
{
var m = prop['rotate'].toString().match(/^(([+-]=)?(-?\d+(\.\d+)?))(.+)?$/);
if (m && m[5])
{
rotateUnits = m[5];
}
prop['rotate'] = m[1];
}
return animateProxied.apply(this, arguments);
}
})(jQuery);

112
js/jquery-css-transform.js vendored Normal file
View File

@ -0,0 +1,112 @@
(function ($) {
// Monkey patch jQuery 1.3.1+ css() method to support CSS 'transform'
// property uniformly across Safari/Chrome/Webkit, Firefox 3.5+, IE 9+, and Opera 11+.
// 2009-2011 Zachary Johnson www.zachstronaut.com
// Updated 2011.05.04 (May the fourth be with you!)
function getTransformProperty(element)
{
// Try transform first for forward compatibility
// In some versions of IE9, it is critical for msTransform to be in
// this list before MozTranform.
var properties = ['transform', 'WebkitTransform', 'msTransform', 'MozTransform', 'OTransform'];
var p;
while (p = properties.shift())
{
if (typeof element.style[p] != 'undefined')
{
return p;
}
}
// Default to transform also
return 'transform';
}
var _propsObj = null;
var proxied = $.fn.css;
$.fn.css = function (arg, val)
{
// Temporary solution for current 1.6.x incompatibility, while
// preserving 1.3.x compatibility, until I can rewrite using CSS Hooks
if (_propsObj === null)
{
if (typeof $.cssProps != 'undefined')
{
_propsObj = $.cssProps;
}
else if (typeof $.props != 'undefined')
{
_propsObj = $.props;
}
else
{
_propsObj = {}
}
}
// Find the correct browser specific property and setup the mapping using
// $.props which is used internally by jQuery.attr() when setting CSS
// properties via either the css(name, value) or css(properties) method.
// The problem with doing this once outside of css() method is that you
// need a DOM node to find the right CSS property, and there is some risk
// that somebody would call the css() method before body has loaded or any
// DOM-is-ready events have fired.
if
(
typeof _propsObj['transform'] == 'undefined'
&&
(
arg == 'transform'
||
(
typeof arg == 'object'
&& typeof arg['transform'] != 'undefined'
)
)
)
{
_propsObj['transform'] = getTransformProperty(this.get(0));
}
// We force the property mapping here because jQuery.attr() does
// property mapping with jQuery.props when setting a CSS property,
// but curCSS() does *not* do property mapping when *getting* a
// CSS property. (It probably should since it manually does it
// for 'float' now anyway... but that'd require more testing.)
//
// But, only do the forced mapping if the correct CSS property
// is not 'transform' and is something else.
if (_propsObj['transform'] != 'transform')
{
// Call in form of css('transform' ...)
if (arg == 'transform')
{
arg = _propsObj['transform'];
// User wants to GET the transform CSS, and in jQuery 1.4.3
// calls to css() for transforms return a matrix rather than
// the actual string specified by the user... avoid that
// behavior and return the string by calling jQuery.style()
// directly
if (typeof val == 'undefined' && jQuery.style)
{
return jQuery.style(this.get(0), arg);
}
}
// Call in form of css({'transform': ...})
else if
(
typeof arg == 'object'
&& typeof arg['transform'] != 'undefined'
)
{
arg[_propsObj['transform']] = arg['transform'];
delete arg['transform'];
}
}
return proxied.apply(this, arguments);
};
})(jQuery);

112
js/jquery-ui-1.8.18.custom.min.js vendored Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

BIN
mute.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

BIN
pingbg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 KiB

10
server/config.coffee Normal file
View File

@ -0,0 +1,10 @@
module.exports =
twitter:
consumer_key: "YOUR TWITTER CONSUMER KEY"
consumer_private_key: "YOUR TWITTER SECRET KEY"
access_token_key: "YOUR TWITTER ONE TIME ACCESS KEY"
access_token_secret: "YOUR TWITTER ONE TIME ACCESS SECRET"
tracker:
database: 'fridgemagnets'
username: 'fridgemagnets'
password: 'some witty password here'

151
server/magnet_server.coffee Normal file
View File

@ -0,0 +1,151 @@
express = require 'express'
mysql = require('db-mysql')
OAuth = require('oauth').OAuth
util = require('util')
config = require('./config')
fs = require('fs')
wordlist = JSON.parse(fs.readFileSync('./wordlist.js', 'utf-8'))
wordstream = (i.w for i in wordlist).join(' ')
body_to_haiku = (lines) ->
ret = (for words in lines
line = words[0].w
for word in words[1...words.length]
line += if word.s == 1 then word.w else ' ' + word.w
line).join(" / ")
console.log(ret)
ret
class AddressTracker
constructor: (database, username, password) ->
@db = new mysql.Database({
"hostname": "localhost"
"user": username
"password": password
"database": database})
@db.on('ready', () -> @connection = this)
@db.on('error', () -> console.log(arguments))
connect: (cb) ->
atrack = @
@db.connect () ->
atrack.connection = this
cb.apply(this, arguments)
validate: (ip_address, message, cb) ->
yesterday = new Date((new Date()).valueOf() - 1000 * 86400)
connection = @connection
connection.query().
select('*').
from('tweets').
where('address = ? and entered > ?', [ip_address, yesterday]).
execute (err, rows, cols) ->
return cb(err, null) if (err)
return cb("You've used up your allotted number of tweets today", null) if rows.length > 10
connection.query().
select('*').
from('tweets').
where('tweet = ?', [body_to_haiku(message.message)]).
execute (err, rows, cols) ->
return cb(err, null) if (err)
return cb("You've already sent that poem!", null) if rows.length > 0
connection.query().
insert('tweets', ['address', 'tweet', 'entered'], [ip_address, body_to_haiku(message.message), (new Date())]).
execute (err, result) ->
return cb(err, null) if err
cb(null, result)
class TwitterPoster
constructor: ->
@oauth = new OAuth(
"https://api.twitter.com/oauth/request_token",
"https://api.twitter.com/oauth/access_token",
config.twitter.consumer_key,
config.twitter.consumer_private_key,
"1.0",
null,
"HMAC-SHA1"
)
post: (message, callback) ->
@oauth.post(
"http://api.twitter.com/1/statuses/update.json",
config.twitter.access_token_key,
config.twitter.access_token_secret,
{"status": body_to_haiku(message.message) },
"application/json",
(error, data, response2) ->
if error
console.log(error) if error
callback(error, null)
return
callback(null, data)
)
app = module.exports = express.createServer()
# Configuration
app.configure ->
app.use express.bodyParser()
app.use express.methodOverride()
app.use express.logger()
app.use app.router
app.configure 'development', ->
app.use express.errorHandler
dumpExceptions: true
showStack: true
app.configure 'production', ->
app.use express.errorHandler()
all_good_words = (lines) ->
for words in lines
for word in words
if not (new RegExp('\\b' + word + '\\b')).test(wordstream)
return false
return true
address_tracker = new AddressTracker(config.tracker.database, config.tracker.username, config.tracker.password)
twitter_poster = new TwitterPoster()
# Our single route
app.post '/poems/', (req, res) ->
if not req.body? or not req.body.message?
res.send({error: true, code: -1, message: "We did not receive a poem."})
return
if not all_good_words(req.body.message)
res.send({error: true, code: -1, message: "ERROR -5: HACKSTOP."})
return
address_tracker.validate req.headers['x-forwarded-for'], req.body, (err, result) ->
if err != null
console.log(err)
res.send({error: true, code: 1, message: err})
return
twitter_poster.post req.body, (err, result) ->
if err != null
console.log(err)
res.send({error: true, code: 2, message: err})
return
res.send({error: false, message: result})
address_tracker.connect () ->
app.listen 8012
console.log "Express server listening on port %d in %s mode", app.address().port, app.settings.env

15
server/package.json Normal file
View File

@ -0,0 +1,15 @@
{
"name": "FridgemagnetServer",
"description": "A Twitter Forwarding Serverour CLI formatting friend.",
"version": "0.0.1",
"author": "Elf M. Sternberg <elf@pendorwright.com>",
"dependencies": {
"coffee-script": "1.2.0",
"oauth": "0.9.6",
"express": "2.5.8",
"forever": "0.8.5",
"db-mysql": "0.7.6"
},
"main": "./magnet_server",
"engines": { "node": ">= 0.6.2" }
}

7
server/schema.sql Normal file
View File

@ -0,0 +1,7 @@
BEGIN;
CREATE TABLE tweets (
address CHAR(42) NOT NULL,
tweet TEXT NOT NULL,
entered DATETIME NOT NULL
);
COMMIT;

36
src/index.haml Normal file
View File

@ -0,0 +1,36 @@
!!! 5
%html{:xmlns => "http://www.w3.org/1999/xhtml"}
%head
%meta{:content => "text/html; charset=utf-8", "http-equiv" => "Content-Type"}/
%meta{"http-equiv" => "Acesss-Control-Allow-Origin", :content => "*"}/
%title Fridge Magnets in HTML5
%link{:href => "style.css", :rel => "stylesheet", :type => "text/css"}/
%link{:href => "ui-lightness/jquery-ui-1.8.18.custom.css", :rel => "stylesheet", :type => "text/css"}/
%body
#board
#results
#footer
#stripe
#muteunmute(data-state='on')
%img(src="unmute.png")
%button(id="shuffler") Shuffle
%p#f1
HTML5 implementation by <a href="http://elfsternberg.com">Elf M. Sternberg</a>. You can see all our poems
<a href="https://twitter.com/#!/html5magnets">@html5magnets</a> on Twitter.
#f2
%div
Comments and feedback to <a href="mailto:elf.sternberg@gmail.com">elf.sternberg@gmail.com</a> | inspired by <a href="http://twittermagnets.com/">twittermagents.com</a> and an allergic reaction to all things flash.</p>
%p The music is <em><span xmlns:dc="http://purl.org/dc/elements/1.1/" href="http://purl.org/dc/dcmitype/Sound" property="dc:title" rel="dc:type">Ethereal Space</span></em> by <a xmlns:cc="http://creativecommons.org/ns#" href="http://ccmixter.org/files/snowflake/33318" property="cc:attributionName" rel="cc:attributionURL">snowflake</a> and is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution (3.0)</a> license.
%div(style="clear:both")/
#message(style="display: none")
%p
%script{:src => "js/jquery-1.7.1.min.js", :type => "text/javascript"}
%script{:src => "js/jquery-ui-1.8.18.custom.min.js", :type => "text/javascript"}
%script{:src => "js/jquery-css-transform.js", :type => "text/javascript"}
%script{:src => "js/jquery-animate-css-rotate-scale.js", :type => "text/javascript"}
%script{:src => "js/buzz.js", :type => "text/javascript"}
%script{:src => "js/sat.js", :type => "text/javascript"}
%script{:src => "js/magnets.js", :type => "text/javascript"}

530
src/magnets.coffee Normal file
View File

@ -0,0 +1,530 @@
SUFFIX = 1
PREFIX = 2
# Average number of words visible on any given iteration.
AVG_VISIBLE = 60
clone = (obj) ->
return obj if not obj? or typeof obj isnt 'object'
newInstance = new obj.constructor()
for key of obj
newInstance[key] = clone obj[key]
newInstance
HEIGHT_FUZZ = 8
WIDTH_FUZZ = 6
# A dimensioned object is one that appears on the board: it has an X
# and Y coordinate, a width and a height. From this, we can create a
# bounding box using the "shape" function. Dimensioned objects can be
# compared to other dimensioned objects to assert whether or not
# they're in collision. Some objects have bounding boxes that pull in
# or push out the borders abstractly, in order to provide for "fuzzy"
# collisions that correspond to drop shadows or similar visual effects.
shape = (x, y, w, h) -> [{x: x, y: y}, {x: x + w, y: y}, {x: x + w, y: y + h}, {x: x, y: y + h}]
class Dimensioned
_width: null
_height: null
_left_p: null
_top_p: null
_left: null
_top: null
_pos: null
constructor: (@el) ->
unset_dims: ->
@_left = @_top = @_width = @_height = @_pos = null
reset_dims: ->
@unset_dims()
[@left(), @top(), @width(), @height()]
positioned: -> return @_width? and @height?
visibleReposition: ->
@reposition()
@el.css {top: @top(), left: @left()}
@
reposition: ->
parent = @el.offsetParent()
[@_top, @_left] = [parseInt(@_top_p * parent.height()), parseInt(@_left_p * parent.width())]
@_pos = {left: @_left, top: @_top}
@
width: -> @_width = if @_width? then @_width else @el.outerWidth()
height: -> @_height = if @_height? then @_height else @el.outerHeight()
pos: -> @_pos = if @_pos? then @_pos else @el.position()
left: -> @_left = if @_left?
@_left
else
@_left = @pos().left
@_left_p = @_left / @el.offsetParent().width()
@_left
top: -> @_top = if @_top?
@_top
else
@_top = @pos().top
@_top_p = @_top / @el.offsetParent().height()
@_top
dims: -> [@width(), @height()]
shape: ->
shape @left(), @top(), @width(), @height()
# I can't decide if this is the right way to go, with a two-pass "set
# it all up, then make it all blow up," but it works quite well, all
# things considered. And after much consideration (like, one minute
# of realizing I never, ever used the features) it became obvious I
# didn't need Dimensioned.
class Heart
constructor: (@parent, @top, @left, symbol) ->
dv = '<div class="heart" style="display:none;top:' + parseInt(@top) + 'px;left:' + \
parseInt(@left) + 'px' + '">' + symbol + '</div>'
@el = $(dv)
@el.css {'font-size': 'larger'} if Math.random() > 0.6
@rot_dist = parseInt(90 * Math.random()) * (if Math.random() < 0.5 then 1 else -1)
[@dir, @dst, @dur] = [Math.random() * 2 * Math.PI, Math.random() * 110, Math.random() * 1200 + 700]
$(@parent).append(@el)
explode: ->
el = $(@el)
el.show().animate({opacity: 0.0, top: parseInt(@top + (Math.sin(@dir) * @dst)), left: parseInt(@left + (Math.cos(@dir) * @dst)), rotate: @rot_dist}, @dur, "easeOutCubic", (() -> el.remove()))
explode_hearts = (@board, @el) ->
randomsymbol = -> ['&#x0266A;','&#x02605;','&#x02736;'][parseInt(Math.random() * 3)]
symbol = if Math.random() < 0.3 then randomsymbol() else '&#x02665;'
parent = @board.el
[top, left, height, width] = [@el.top(), @el.left(), @el.height(), @el.width()]
hearts = for i in [0..(22 + (6 - Math.floor(Math.random() * 12)))]
new Heart(parent, top + (0.5 * height), left + (0.5 * width), symbol)
(h.explode() for h in hearts)
# The board is the principle object on which all other objects are
# dependent. I decided to make it a 'Dimensioned' because I'm going
# to be constantly querying its height and width.
class Board extends Dimensioned
append: (ob) -> @el.append(ob)
css: (width, height) ->
@el.css
width: width
height: height
@reset_dims()
class Footer extends Dimensioned
# A Tile is a word tile. It has a single word.
class Tile extends Dimensioned
base_style:
'font-size': "15px"
drag_style:
'font-size': "19px"
visible: false
# Initial tilt.
rotation: (Math.random() * 30) - 15
constructor: (@word, @board, @master) ->
@el = $('<div class="word">' + @word.w + '</div>')
@el.css @base_style
@board.append(@el)
@rotation = (Math.random() * 30) - 15
@el.draggable
helper: "original"
refreshPositions: false
revertDuration: 1
start: (event) =>
mod = (Math.random() * 16) - 8
@rotation = if Math.abs(@rotation + mod) > 15 then @rotation - mod else @rotation + mod
style = clone(@drag_style)
style.rotate = @rotation
@el.animate(style, 200, () => @new_width = @el.width())
true
stop: (event) =>
# Drop the thing dead center, at least on the x-axis,
# and animate its return to the new font size.
mod = (Math.random() * 16) - 8
@rotation = if Math.abs(@rotation + mod) > 15 then @rotation - mod else @rotation + mod
style = clone(@base_style)
style.rotate = @rotation
style['left'] = parseInt(@el.position().left + (0.5 * (@new_width - @width())))
@el.animate style, 200, 'easeOutQuad', () =>
@reset_dims()
explode_hearts(@board, @)
@master.poemed(@)
true
fadeOut: -> $.Deferred((d) => @el.fadeOut('fast', (() => @unset_dims(); @visible = false; d.resolve()))).promise()
# Shape for deteriming poemed collision
fuzzyshape: -> shape @left() - WIDTH_FUZZ, @top() - HEIGHT_FUZZ, @width() + (2 * WIDTH_FUZZ), @height() + (2 * HEIGHT_FUZZ)
get_new_pos: ->
bh = => parseInt(Math.random() * (@board.height() - @height()) * 0.985)
bw = => parseInt(Math.random() * (@board.width() - @width()) * 0.98)
[top, left] = [bh(), bw()]
[top, left] = [bh(), bw()] until @master.unoccupied(left, top, @width(), @height())
[top, left]
flyIn: ->
fd = (mod) ->
m = parseInt(40 * Math.random())
if (Math.random() < 0.5) then mod + m else -1 * m
@el.css
left: fd(@board.width())
top: fd(@board.height())
dfd = $.Deferred()
x = Math.random()
[top, left] = @get_new_pos()
@el.fadeIn().animate {top: top, left: left, rotate: @rotation}, 1500, 'easeOutQuint', () =>
@visible = true
dfd.resolve()
dfd.promise()
class PoemDisplay extends Dimensioned
el: $('#results')
_max_box: null
dialog: $('#message')
dtimer: null
constructor: (@board) ->
@el.css({top: @board.height()})
sentSuccess: (data, textStatus) =>
$('p', @dialog).html "Your poem has been immortalized! It can be seen on Twitter at <a href='https://twitter.com/#!/html5magnets'>@html5magnets</a>."
if data.error
$('p', @dialog).html data.message
@dialog.dialog("open")
if dtimer != null
clearTimeout(dtimer)
dtimer = null
dtimer = setTimeout (() => @dialog.dialog("close")), 7500
sentError: (query, textStatus) =>
console.log(query, textStatus)
sendToServer: (haiku) =>
$.ajax 'http://html5magnets.elfsternberg.com/poems/',
type: "POST"
data: {"message": haiku}
dataType: 'json'
success: @sentSuccess
error: @sentError
update: (lines) ->
lines = (l for l in lines when l.length > 0)
if lines.length == 0
@el.fadeOut()
return
@el.html('')
@el.show()
res = for words in lines
line = words[0].w
for word in words[1...words.length]
line += if word.s == 1 then word.w else '&nbsp;' + word.w
@el.append($('<p>' + line + '</p>'))
sentence = for words in lines
line = words[0].w
for word in words[1...words.length]
line += if word.s == 1 then word.w else ' ' + word.w
line
haiku_add = 0
if sentence.length > 1
haiku = sentence.join(" / ")
if haiku.length < 140
haiku_add = 38
@el.append('<div id="tweetthis"><img src="tweetthis.png"></div>')
$('#tweetthis').click(() => @sendToServer(lines))
if lines.length != @lastlines
lh = $('p', @el).height()
setTimeout((() => @el.animate {top: @board.height() - ((lh * (lines.length + 1.7)) + haiku_add)}), 1)
@
max_box: =>
return shape(@board.height() - (16 * 6.7), 0, 480, (16 * 6.7))
# A poem is three or more *moved* words in fuzzy collision.
class Poem
words: []
constructor: (@master) ->
@poembox = new PoemDisplay(@master.board)
real_poem: (poem = null) ->
poem = @words if not poem?
if poem.length > 1 then poem else []
has: (word) ->
return (w for w in @words when w == word).length > 0
find_bbox: (words = null, sp = 0) ->
words = @words if not words
return null if words.length < 2
[ul, ur, lr, ll] = words[0].shape()
[mx, my, nx, ny] = [ul.x, ul.y, lr.x, lr.y]
for i in [1...words.length]
[ul1, ur1, lr1, ll1] = words[i].shape()
mx = ul1.x if ul1.x < mx
my = ul1.y if ul1.y < my
nx = lr1.x if lr1.x > nx
ny = lr1.y if lr1.y > ny
return [{x: mx - sp, y: my - sp}, {x: nx + sp, y: my - sp}, {x: nx + sp, y: ny + sp}, {x: nx + sp, y: my - sp}]
check_dismissal: (word) ->
# If the word is colliding with another word in the poem, it
# is not being dismissed.
fuzzyshape = word.fuzzyshape()
for w in @words
if w != word and colliding(fuzzyshape, w.fuzzyshape())
@inorder()
return @words
# Remove word from @words
@words = @real_poem(w for w in @words when w != word)
return @words if @words.length < 2
# Reconstitute poem from what remains
find_split_poem = (poem) =>
# Why 2? Because a poem of length 1 is just a word!
throw "Don't run on an empty poem!" if poem.length < 2
# Transfer all words in *poem2* that are in collision with
# words in poem1. If the poems don't change, return them,
# otherwise repeat the process.
edgefollow = (poem1, poem2) =>
to_xfr = (w2 for w2 in poem2 when \
((w1 for w1 in poem1 when \
colliding(w1.fuzzyshape(), w2.fuzzyshape())).length > 0))
# Words are not being shuffled around
return [poem1, poem2] if to_xfr.length == 0
# Else...
poem1 = poem1.concat(to_xfr)
poem2 = (w for w in poem2 when w not in poem1)
edgefollow(poem1, poem2)
wordlist = (i for i in poem)
first_word = wordlist.pop()
[lpoem, rpoem] = edgefollow([first_word], wordlist)
return [] if lpoem.length < 2 and lpoem.length < 2
return rpoem if lpoem.length < 2
return lpoem if rpoem.length < 2
return if Math.vector.magnitude(@find_bbox(lpoem)[0]) < Math.vector.magnitude(@find_bbox(rpoem)[0])
lpoem
else
rpoem
@words = @real_poem(find_split_poem(@words))
if @words
@inorder()
@words
# Looks at the bounding box for the current poem and adds any words
# to it that are in collision with the existing poem.
# :: [tiles] -> [tiles]
research_poem: (poem) ->
nbbox = @find_bbox(poem)
newpoem = (i for i in poem)
potentials = (w for w in @master.visible() when \
(w not in newpoem) and colliding(w.fuzzyshape(), nbbox))
# [word, poem] -> boolean
collides_with_existing_poem = (nw1, poem1) ->
fzs1 = nw1.fuzzyshape()
acw1 = nw1.word
((nw2 for nw2 in poem1 when \
acw1 != nw2.word and \
colliding(nw2.fuzzyshape(), fzs1)).length > 0)
addenda = (nw for nw in potentials when collides_with_existing_poem(nw, newpoem))
if addenda.length == 0 then newpoem else @research_poem(newpoem.concat(addenda))
# Looks to see if the word has come into collision with another
# word, creating a new poem.
# :: tile -> [tiles]
maybe_new_poem: (word) ->
throw "Do not call maybe_new_poem on a working poem." if @words.length > 0
fuzzyshape = word.fuzzyshape()
@words = @real_poem((w for w in @master.visible() when \
colliding(w.fuzzyshape(), fuzzyshape)))
if @words.length
@words = @research_poem(@words)
@inorder()
@words
check_for_addition: (word) ->
# See if this word collides with any of the words in our poem:
fuzzyshape = word.fuzzyshape()
for w in @words
if colliding(fuzzyshape, w.fuzzyshape()) and w != word
@words.push(word)
# One collision is all it takes.
break
@words = @research_poem(@words)
@inorder()
@words
check: (word) ->
return @words = @maybe_new_poem(word) if @words.length == 0
if @has(word)
@words = @check_dismissal(word)
return @words = if @words.length == 0 then @maybe_new_poem(word) else @words
# This word doesn't create a new poem, and it isn't present in
# our existing poem.
return @words = @check_for_addition(word)
inorder: ->
return @poembox.update([]) if @words.length < 2
nbbox = @find_bbox(@words)
avg_height = 0
for w in @words
avg_height = avg_height + w.height()
avg_height = parseInt(avg_height / @words.length)
ret = []
for i in (i for i in [nbbox[0].y...nbbox[2].y] by avg_height)
zbot = i + avg_height
zone_words = (w for w in @words when w.top() >= i and w.top() < zbot)
zone_words.sort (a, b) -> a.left() - b.left()
ret.push((i.word for i in zone_words))
@poembox.update(ret)
class Magnets extends Dimensioned
constructor: (@wordlist) ->
@el = $(window)
@footer = new Footer($('#footer'))
@board = new Board($('#board'))
@recbox = $('#recbox')
@results = $('#results')
@words = (new Tile(word, @board, @) for word in @wordlist)
@resize()
@poem = new Poem(@)
$('#shuffler').click(@reword)
$(window).resize(@resize)
resize: =>
@unset_dims()
@board.css('100%', @height() - @footer.height())
(word.visibleReposition() for word in @words when word.visible)
@
unoccupied:(left, top, width, height) ->
reserved = []
if @poem.real_poem().length > 0
reserved.push(@poem.find_bbox(null, 10))
reserved.push(@poem.poembox.max_box())
target = shape(left, top, width, height)
for s in reserved
if colliding(target, s)
return false
true
visible: ->
(w for w in @words when w.visible)
poemed: (word) ->
@poem.check(word)
livewords: -> (w for w in @words when w.visible)
reword: =>
poemed = (w for w in @words when @poem.has(w))
flyprob = AVG_VISIBLE / (@words.length - poemed.length)
$.when.apply(null, (w.fadeOut() for w in @words when not @poem.has(w))).then () =>
$.when.apply(null, (w.flyIn() for w in @words when not @poem.has(w) and Math.random() < flyprob)).then () =>
(w.reset_dims() for w in @words when w.visible)
@
class MusicPlayer
constructor: (control, tunes) ->
@control = $(control)
@control.data('state', 'on')
@active = true
@music = new buzz.sound(tunes, {preload:true, autoload: true, loop: true})
@music.setVolume(0)
@music.bind 'canplaythrough', () =>
@music.play()
@music.fadeTo(60, 10000)
@control.click (ev) =>
@active = if @active then @fadeOut() else @fadeIn()
fadeOut: ->
@music.fadeOut(600, () => @music.pause())
$('img', @control).attr('src', 'mute.png')
false
fadeIn: ->
@music.play().fadeIn(1200)
$('img', @control).attr('src', 'unmute.png')
true
$ ->
$.ajax
url: 'js/wordlist.js'
data: {}
success: (data) -> (new Magnets(data)).resize().reword()
error: -> console.log(arguments)
dataType: 'json'
v = new MusicPlayer('#muteunmute',
['media/snowflake_-_Ethereal_Space.mp3',
'media/snowflake_-_Ethereal_Space.ogg'])
$( "#message" ).dialog
autoOpen: false
show: "fadeIn"
hide: "fadeOut"

97
src/sat.coffee Normal file
View File

@ -0,0 +1,97 @@
#Copyright (c) 2012 Elf M. Sternberg
#
# Much of the code here I would never have understood if it hadn't
# been for the patient work of Caleb Helbling
# (http://www.propulsionjs.com/), as well as the Wikipedia pages for
# the Separating Axis Theorem. It took me a week to wrap my head
# around these ideas.
#
#Permission is hereby granted, free of charge, to any person obtaining a copy
#of this software and associated documentation files (the "Software"), to deal
#in the Software without restriction, including without limitation the rights
#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
#copies of the Software, and to permit persons to whom the Software is
#furnished to do so, subject to the following conditions:
#
#The above copyright notice and this permission notice shall be included in
#all copies or substantial portions of the Software.
#
#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
#THE SOFTWARE.
Math.vector =
add: (v1, v2) -> {x: (v1.x + v2.x), y: (v1.y + v2.y)}
# Scale a given vector.
scalar: (v, s) -> {x: (v.x * s), y: (v.y * s)}
dot: (v1, v2) -> v1.x * v2.x + v1.y * v2.y
magnitude2: (v) ->
x = v.x
y = v.y
x * x + y * y
magnitude: (v) -> Math.sqrt(Math.vector.magnitude2(v))
normalize: (v) ->
mag = Math.vector.magnitude(v)
{x: (v.x / mag), y: (v.y / mag)}
leftNormal: (v) -> {x: -v.y, y: v.x}
this.colliding = (shape1, shape2) ->
# Return the axes of a shape. In a polygon, each potential
# separating axis is the normal to each edge. For our purposes, a
# "shape" is an array of points with the structure [{x: 0, y: 0}, .. ]
# We assume that the final edge is from the last point back to the
# first.
genAxes = (shape) ->
throw "Cannot handle non-polygons" if shape.length < 3
# Calculate the normal of a single pair of points in the
# shape.
axis = (shape, pi) ->
p1 = shape[pi]
p2 = shape[if pi == (shape.length - 1) then 0 else pi + 1]
edge = {x: p1.x - p2.x, y: p1.y - p2.y}
Math.vector.normalize(Math.vector.leftNormal(edge))
(axis(shape, i) for i in [0...shape.length])
# Calculate the extremis of the shape "above" a given axis
genProjection = (shape, axis) ->
min = Math.vector.dot(axis, shape[0])
max = min
for i in [1...shape.length]
p = Math.vector.dot(axis, shape[i])
min = p if p < min
max = p if p > max
{min: min, max: max}
axes1 = genAxes(shape1)
axes2 = genAxes(shape2)
axes = axes1.concat axes2
for axis in axes
proj1 = genProjection(shape1, axis)
proj2 = genProjection(shape2, axis)
if not ( \
(proj1.min >= proj2.min and proj1.min <= proj2.max) or \
(proj1.max >= proj2.min and proj1.max <= proj2.max) or \
(proj2.min >= proj1.min and proj2.min <= proj1.max) or \
(proj2.max >= proj1.min and proj2.max <= proj1.max))
return false
return true

269
src/style.less Normal file
View File

@ -0,0 +1,269 @@
/* -*- mode: css; -*- */
/* ___ ___ ___ ___ _
/ __/ __/ __| | _ \___ ___ ___| |_
| (__\__ \__ \ | / -_|_-</ -_) _|
\___|___/___/ |_|_\___/__/\___|\__|
html5doctor.com Reset Stylesheet
v1.5
Last Updated: 2010-08-12
Author: Richard Clark - http://richclarkdesign.com
Twitter: @rich_clark
*/
html, body, div, span, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
abbr, address, cite, code,
del, dfn, em, img, ins, kbd, q, samp,
small, strong, sub, sup, var,
b, i,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, figcaption, figure,
footer, header, hgroup, menu, nav, section, summary,
time, mark, audio, video {
margin:0;
padding:0;
border:0;
outline:0;
vertical-align:baseline;
background:transparent;
}
body {
line-height:1;
}
article,aside,canvas,details,figcaption,figure,
footer,header,hgroup,menu,nav,section,summary {
display:block;
}
nav ul {
list-style:none;
}
blockquote, q {
quotes:none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content:'';
content:none;
}
a {
margin:0;
padding:0;
font-size:100%;
vertical-align:baseline;
background:transparent;
}
a {
margin:0;
padding:0;
font-size:100%;
vertical-align:baseline;
background:transparent;
}
ins {
background-color:#ff9;
color:#000;
text-decoration:none;
}
mark {
background-color:#ff9;
color:#000;
font-style:italic;
font-weight:bold;
}
del {
text-decoration: line-through;
}
abbr[title], dfn[title] {
border-bottom:1px dotted #000;
cursor:help;
}
table {
border-collapse:collapse;
border-spacing:0;
}
hr {
display:block;
height:1px;
border:0;
border-top:1px solid #cccccc;
margin:1em 0;
padding:0;
}
input, select {
vertical-align:middle;
}
.small-rounded {
-moz-border-radius-topleft: 5px;
-moz-border-radius-topright: 5px;
-moz-border-radius-bottomleft: 5px;
-moz-border-radius-bottomright: 5px;
-webkit-border-bottom-right-radius: 5px;
-webkit-border-top-left-radius: 5px;
-webkit-border-top-right-radius: 5px;
-webkit-border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
border-bottom-left-radius: 5px;
}
html {
overflow: hidden;
}
@limegreen: #32cd32;
#board {
overflow: hidden;
position: relative;
width: 100%;
background: url('pingbg.png') repeat;
}
.word {
font-family: Georgia, Palatino,"Palatino Linotype", Times, "Times New Roman", serif;
-moz-box-shadow: 0 0 6px 2px #aaa;
-webkit-box-shadow: 0 0 6px 2px #aaa;
box-shadow: 0 0 6px 2px #aaa;
display: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-o-user-select: none;
text-align: center;
user-select: none;
cursor: pointer;
color: #444;
font-size: 13px;
padding: 3px 4px 4px 4px;
position: absolute;
background: white;
z-index: 100;
}
#footer {
height: 18ex;
font-family: "Trebuchet MS", "Lucida Sans Unicode", "Lucida Grande", "Lucida Sans", Arial, sans-serif;
font-size: 12px;
width: 100%;
}
#stripe {
width: 100%;
margin: 0.4% 0 0.4% 0;
background-color: @limegreen;
height: 10ex;
}
.heart {
position: absolute;
color: deeppink;
font-size: 22px;
font-weight: bold;
z-index: 30;
}
#f1 { width: 46%; float: left; padding-left: 1%;}
#f2 { width: 46%; float: right; text-align: right; padding-right: 1%;}
.recline {
position: relative;
height: 40px;
width: 100%;
clear: right;
background: url(writerect_bg.png) top left repeat-x;
}
#results {
-moz-border-radius-topright: 5px;
-webkit-border-top-right-radius: 5px;
border-top-right-radius: 5px;
border-top: 1px solid #888;
border-right: 1px solid #888;
border-bottom: 1px solid #888;
position: absolute;
color: #666;
left: 0px;
bottom: 5px;
padding: 1em;
background: url(alphamod.png) repeat;
display: none;
p {
white-space: nowrap;
line-height: 1.1;
padding: 0;
}
}
#shuffler {
font-family: Georgia, Palatino,"Palatino Linotype", Times, "Times New Roman", serif;
font-size: 16px;
font-weight: bold;
color: white;
position: relative;
background-color: @limegreen;
margin-top: 10px;
float: right;
cursor: pointer;
margin-right: 32px;
width: 5em;
vertical-align: middle;
border-top: 1px solid white;
border-left: 1px solid white;
border-bottom: 1px solid black;
border-right: 1px solid black;
box-shadow: 1px 3px 6px rgba(0, 0, 0, 0.80);
-moz-box-shadow: 1px 3px 6px rgba(0, 0, 0, 0.80);
-webkit-box-shadow: 1px 3px 6px rgba(0, 0, 0, 0.80);
}
#shuffler:hover {
background-color: darken(@limegreen, 10%);
}
#shuffler:active {
background-color: darken(@limegreen, 10%);
border-top: 1px solid black;
border-left: 1px solid black;
border-bottom: 1px solid white;
border-right: 1px solid white;
}
#muteunmute {
cursor: pointer;
float: right;
padding-right: 128px;
width: 42px;
height: 42px;
img {
width: 42px;
height: 42px;
}
}
#tweetthis {
padding-top: 6px;
}

34
src/test_sat.coffee Normal file
View File

@ -0,0 +1,34 @@
testCase = require('nodeunit').testCase
require('./sat.coffee')
module.exports = testCase
"TestAddition": (test) ->
m = Math.vector.add {x: 1, y: 1}, {x: -1, y: -1}
test.ok(m.x == 0 and m.y == 0)
m = Math.vector.add {x: 1, y: 1}, {x: 1, y: 1}
test.ok(m.x == 2 and m.y == 2)
test.done()
"TestScalar": (test) ->
m = Math.vector.scalar({x: 2, y: 2}, 2)
test.ok(m.x == 4 and m.y == 4)
test.done()
"TestMag2": (test) ->
m = Math.vector.magnitude2({x: 2, y: 2})
test.ok(m == 8)
test.done()
"TestMag": (test) ->
m = Math.vector.magnitude({x: 2, y: 2})
test.ok(m == Math.sqrt(8))
test.done()
"TestNormalize": (test) ->
m = Math.vector.normalize({x: 5, y: 0})
test.ok(m.x == 1 and m.y == 0)
m = Math.vector.normalize({x: 0, y: 5})
test.ok(m.x == 0 and m.y == 1)
m = Math.vector.normalize({x: 4, y: 3})
test.ok((m.x * m.x + m.y * m.y) == 1)
test.done()

210
src/wordlist.coffee Normal file
View File

@ -0,0 +1,210 @@
[{"w": "a", "s": 0},
{"w": "a", "s": 0},
{"w": "about", "s": 0},
{"w": "above", "s": 0},
{"w": "after", "s": 0},
{"w": "all", "s": 0},
{"w": "almost", "s": 0},
{"w": "always", "s": 0},
{"w": "am", "s": 0},
{"w": "an", "s": 0},
{"w": "an", "s": 0},
{"w": "and", "s": 0},
{"w": "and", "s": 0},
{"w": "animal", "s": 0},
{"w": "apple", "s": 0},
{"w": "are", "s": 0},
{"w": "as", "s": 0},
{"w": "as", "s": 0},
{"w": "ask", "s": 0},
{"w": "at", "s": 0},
{"w": "bad", "s": 0},
{"w": "be", "s": 0},
{"w": "beauty", "s": 0},
{"w": "believe", "s": 0},
{"w": "beneath", "s": 0},
{"w": "between", "s": 0},
{"w": "bird", "s": 0},
{"w": "birthday", "s": 0},
{"w": "blend", "s": 0},
{"w": "blue", "s": 0},
{"w": "bring", "s": 0},
{"w": "but", "s": 0},
{"w": "but", "s": 0},
{"w": "butterfly", "s": 0},
{"w": "by", "s": 0},
{"w": "calendar", "s": 0},
{"w": "can", "s": 0},
{"w": "celebrate", "s": 0},
{"w": "change", "s": 0},
{"w": "cloud", "s": 0},
{"w": "cold", "s": 0},
{"w": "come", "s": 0},
{"w": "comfort", "s": 0},
{"w": "could", "s": 0},
{"w": "d", "s": 1},
{"w": "dark", "s": 0},
{"w": "day", "s": 0},
{"w": "delightful", "s": 0},
{"w": "desire", "s": 0},
{"w": "did", "s": 0},
{"w": "do", "s": 0},
{"w": "dream", "s": 0},
{"w": "e", "s": 1},
{"w": "eat", "s": 0},
{"w": "ed", "s": 1},
{"w": "er", "s": 1},
{"w": "es", "s": 1},
{"w": "est", "s": 1},
{"w": "evening", "s": 0},
{"w": "every", "s": 0},
{"w": "fall", "s": 0},
{"w": "favorite", "s": 0},
{"w": "feel", "s": 0},
{"w": "float", "s": 0},
{"w": "flower", "s": 0},
{"w": "for", "s": 0},
{"w": "from", "s": 0},
{"w": "full", "s": 0},
{"w": "fun", "s": 0},
{"w": "garden", "s": 0},
{"w": "get", "s": 0},
{"w": "ghost", "s": 0},
{"w": "good", "s": 0},
{"w": "grass", "s": 0},
{"w": "green", "s": 0},
{"w": "grow", "s": 0},
{"w": "happy", "s": 0},
{"w": "has", "s": 0},
{"w": "have", "s": 0},
{"w": "he", "s": 0},
{"w": "here", "s": 0},
{"w": "here", "s": 0},
{"w": "him", "s": 0},
{"w": "his", "s": 0},
{"w": "hot", "s": 0},
{"w": "house", "s": 0},
{"w": "how", "s": 0},
{"w": "I", "s": 0},
{"w": "I", "s": 0},
{"w": "if", "s": 0},
{"w": "in", "s": 0},
{"w": "ing", "s": 1},
{"w": "ing", "s": 1},
{"w": "is", "s": 0},
{"w": "is", "s": 0},
{"w": "it", "s": 0},
{"w": "keep", "s": 0},
{"w": "laugh", "s": 0},
{"w": "learn", "s": 0},
{"w": "leave", "s": 0},
{"w": "let", "s": 0},
{"w": "light", "s": 0},
{"w": "like", "s": 0},
{"w": "like", "s": 0},
{"w": "live", "s": 0},
{"w": "long", "s": 0},
{"w": "look", "s": 0},
{"w": "love", "s": 0},
{"w": "ly", "s": 1},
{"w": "magic", "s": 0},
{"w": "make", "s": 0},
{"w": "man", "s": 0},
{"w": "me", "s": 0},
{"w": "memory", "s": 0},
{"w": "month", "s": 0},
{"w": "more", "s": 0},
{"w": "morning", "s": 0},
{"w": "must", "s": 0},
{"w": "my", "s": 0},
{"w": "never", "s": 0},
{"w": "nibble", "s": 0},
{"w": "night", "s": 0},
{"w": "no", "s": 0},
{"w": "of", "s": 0},
{"w": "of", "s": 0},
{"w": "off", "s": 0},
{"w": "on", "s": 0},
{"w": "only", "s": 0},
{"w": "or", "s": 0},
{"w": "out", "s": 0},
{"w": "out", "s": 0},
{"w": "paint", "s": 0},
{"w": "people", "s": 0},
{"w": "perfect", "s": 0},
{"w": "play", "s": 0},
{"w": "proof", "s": 0},
{"w": "puff", "s": 0},
{"w": "r", "s": 1},
{"w": "rain", "s": 0},
{"w": "room", "s": 0},
{"w": "s", "s": 1},
{"w": "s", "s": 1},
{"w": "s", "s": 1},
{"w": "say", "s": 0},
{"w": "season", "s": 0},
{"w": "see", "s": 0},
{"w": "she", "s": 0},
{"w": "shine", "s": 0},
{"w": "simple", "s": 0},
{"w": "sky", "s": 0},
{"w": "snow", "s": 0},
{"w": "so", "s": 0},
{"w": "some", "s": 0},
{"w": "song", "s": 0},
{"w": "spring", "s": 0},
{"w": "summer", "s": 0},
{"w": "sun", "s": 0},
{"w": "sweet", "s": 0},
{"w": "take", "s": 0},
{"w": "talk", "s": 0},
{"w": "than", "s": 0},
{"w": "that", "s": 0},
{"w": "the", "s": 0},
{"w": "the", "s": 0},
{"w": "their", "s": 0},
{"w": "then", "s": 0},
{"w": "there", "s": 0},
{"w": "they", "s": 0},
{"w": "this", "s": 0},
{"w": "though", "s": 0},
{"w": "through", "s": 0},
{"w": "time", "s": 0},
{"w": "to", "s": 0},
{"w": "to", "s": 0},
{"w": "together", "s": 0},
{"w": "too", "s": 0},
{"w": "touch", "s": 0},
{"w": "trick", "s": 0},
{"w": "truth", "s": 0},
{"w": "up", "s": 0},
{"w": "us", "s": 0},
{"w": "use", "s": 0},
{"w": "vacation", "s": 0},
{"w": "walk", "s": 0},
{"w": "want", "s": 0},
{"w": "warm", "s": 0},
{"w": "was", "s": 0},
{"w": "watch", "s": 0},
{"w": "we", "s": 0},
{"w": "weather", "s": 0},
{"w": "were", "s": 0},
{"w": "when", "s": 0},
{"w": "which", "s": 0},
{"w": "whisper", "s": 0},
{"w": "who", "s": 0},
{"w": "why", "s": 0},
{"w": "will", "s": 0},
{"w": "winter", "s": 0},
{"w": "with", "s": 0},
{"w": "woman", "s": 0},
{"w": "word", "s": 0},
{"w": "work", "s": 0},
{"w": "world", "s": 0},
{"w": "would", "s": 0},
{"w": "y", "s": 1},
{"w": "year", "s": 0},
{"w": "you", "s": 0},
{"w": "you", "s": 0},
{"w": "your", "s": 0}
]

BIN
tweetthis.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

326
ui-lightness/jquery-ui-1.8.18.custom.css vendored Normal file
View File

@ -0,0 +1,326 @@
/*
* jQuery UI CSS Framework 1.8.18
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Theming/API
*/
/* Layout helpers
----------------------------------*/
.ui-helper-hidden { display: none; }
.ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); }
.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
.ui-helper-clearfix:before, .ui-helper-clearfix:after { content: ""; display: table; }
.ui-helper-clearfix:after { clear: both; }
.ui-helper-clearfix { zoom: 1; }
.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
/* Interaction Cues
----------------------------------*/
.ui-state-disabled { cursor: default !important; }
/* Icons
----------------------------------*/
/* states and images */
.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }
/* Misc visuals
----------------------------------*/
/* Overlays */
.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
/*
* jQuery UI CSS Framework 1.8.18
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Theming/API
*
* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Trebuchet%20MS,%20Tahoma,%20Verdana,%20Arial,%20sans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=f6a828&bgTextureHeader=12_gloss_wave.png&bgImgOpacityHeader=35&borderColorHeader=e78f08&fcHeader=ffffff&iconColorHeader=ffffff&bgColorContent=eeeeee&bgTextureContent=03_highlight_soft.png&bgImgOpacityContent=100&borderColorContent=dddddd&fcContent=333333&iconColorContent=222222&bgColorDefault=f6f6f6&bgTextureDefault=02_glass.png&bgImgOpacityDefault=100&borderColorDefault=cccccc&fcDefault=1c94c4&iconColorDefault=ef8c08&bgColorHover=fdf5ce&bgTextureHover=02_glass.png&bgImgOpacityHover=100&borderColorHover=fbcb09&fcHover=c77405&iconColorHover=ef8c08&bgColorActive=ffffff&bgTextureActive=02_glass.png&bgImgOpacityActive=65&borderColorActive=fbd850&fcActive=eb8f00&iconColorActive=ef8c08&bgColorHighlight=ffe45c&bgTextureHighlight=03_highlight_soft.png&bgImgOpacityHighlight=75&borderColorHighlight=fed22f&fcHighlight=363636&iconColorHighlight=228ef1&bgColorError=b81900&bgTextureError=08_diagonals_thick.png&bgImgOpacityError=18&borderColorError=cd0a0a&fcError=ffffff&iconColorError=ffd27a&bgColorOverlay=666666&bgTextureOverlay=08_diagonals_thick.png&bgImgOpacityOverlay=20&opacityOverlay=50&bgColorShadow=000000&bgTextureShadow=01_flat.png&bgImgOpacityShadow=10&opacityShadow=20&thicknessShadow=5px&offsetTopShadow=-5px&offsetLeftShadow=-5px&cornerRadiusShadow=5px
*/
/* Component containers
----------------------------------*/
.ui-widget { font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif; font-size: 1.1em; }
.ui-widget .ui-widget { font-size: 1em; }
.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif; font-size: 1em; }
.ui-widget-content { border: 1px solid #dddddd; background: #eeeeee url(images/ui-bg_highlight-soft_100_eeeeee_1x100.png) 50% top repeat-x; color: #333333; }
.ui-widget-content a { color: #333333; }
.ui-widget-header { border: 1px solid #e78f08; background: #f6a828 url(images/ui-bg_gloss-wave_35_f6a828_500x100.png) 50% 50% repeat-x; color: #ffffff; font-weight: bold; }
.ui-widget-header a { color: #ffffff; }
/* Interaction states
----------------------------------*/
.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #cccccc; background: #f6f6f6 url(images/ui-bg_glass_100_f6f6f6_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #1c94c4; }
.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #1c94c4; text-decoration: none; }
.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #fbcb09; background: #fdf5ce url(images/ui-bg_glass_100_fdf5ce_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #c77405; }
.ui-state-hover a, .ui-state-hover a:hover { color: #c77405; text-decoration: none; }
.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #fbd850; background: #ffffff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #eb8f00; }
.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #eb8f00; text-decoration: none; }
.ui-widget :active { outline: none; }
/* Interaction Cues
----------------------------------*/
.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #fed22f; background: #ffe45c url(images/ui-bg_highlight-soft_75_ffe45c_1x100.png) 50% top repeat-x; color: #363636; }
.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636; }
.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cd0a0a; background: #b81900 url(images/ui-bg_diagonals-thick_18_b81900_40x40.png) 50% 50% repeat; color: #ffffff; }
.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #ffffff; }
.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #ffffff; }
.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; }
.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }
/* Icons
----------------------------------*/
/* states and images */
.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png); }
.ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); }
.ui-widget-header .ui-icon {background-image: url(images/ui-icons_ffffff_256x240.png); }
.ui-state-default .ui-icon { background-image: url(images/ui-icons_ef8c08_256x240.png); }
.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_ef8c08_256x240.png); }
.ui-state-active .ui-icon {background-image: url(images/ui-icons_ef8c08_256x240.png); }
.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_228ef1_256x240.png); }
.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_ffd27a_256x240.png); }
/* positioning */
.ui-icon-carat-1-n { background-position: 0 0; }
.ui-icon-carat-1-ne { background-position: -16px 0; }
.ui-icon-carat-1-e { background-position: -32px 0; }
.ui-icon-carat-1-se { background-position: -48px 0; }
.ui-icon-carat-1-s { background-position: -64px 0; }
.ui-icon-carat-1-sw { background-position: -80px 0; }
.ui-icon-carat-1-w { background-position: -96px 0; }
.ui-icon-carat-1-nw { background-position: -112px 0; }
.ui-icon-carat-2-n-s { background-position: -128px 0; }
.ui-icon-carat-2-e-w { background-position: -144px 0; }
.ui-icon-triangle-1-n { background-position: 0 -16px; }
.ui-icon-triangle-1-ne { background-position: -16px -16px; }
.ui-icon-triangle-1-e { background-position: -32px -16px; }
.ui-icon-triangle-1-se { background-position: -48px -16px; }
.ui-icon-triangle-1-s { background-position: -64px -16px; }
.ui-icon-triangle-1-sw { background-position: -80px -16px; }
.ui-icon-triangle-1-w { background-position: -96px -16px; }
.ui-icon-triangle-1-nw { background-position: -112px -16px; }
.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
.ui-icon-arrow-1-n { background-position: 0 -32px; }
.ui-icon-arrow-1-ne { background-position: -16px -32px; }
.ui-icon-arrow-1-e { background-position: -32px -32px; }
.ui-icon-arrow-1-se { background-position: -48px -32px; }
.ui-icon-arrow-1-s { background-position: -64px -32px; }
.ui-icon-arrow-1-sw { background-position: -80px -32px; }
.ui-icon-arrow-1-w { background-position: -96px -32px; }
.ui-icon-arrow-1-nw { background-position: -112px -32px; }
.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
.ui-icon-arrow-4 { background-position: 0 -80px; }
.ui-icon-arrow-4-diag { background-position: -16px -80px; }
.ui-icon-extlink { background-position: -32px -80px; }
.ui-icon-newwin { background-position: -48px -80px; }
.ui-icon-refresh { background-position: -64px -80px; }
.ui-icon-shuffle { background-position: -80px -80px; }
.ui-icon-transfer-e-w { background-position: -96px -80px; }
.ui-icon-transferthick-e-w { background-position: -112px -80px; }
.ui-icon-folder-collapsed { background-position: 0 -96px; }
.ui-icon-folder-open { background-position: -16px -96px; }
.ui-icon-document { background-position: -32px -96px; }
.ui-icon-document-b { background-position: -48px -96px; }
.ui-icon-note { background-position: -64px -96px; }
.ui-icon-mail-closed { background-position: -80px -96px; }
.ui-icon-mail-open { background-position: -96px -96px; }
.ui-icon-suitcase { background-position: -112px -96px; }
.ui-icon-comment { background-position: -128px -96px; }
.ui-icon-person { background-position: -144px -96px; }
.ui-icon-print { background-position: -160px -96px; }
.ui-icon-trash { background-position: -176px -96px; }
.ui-icon-locked { background-position: -192px -96px; }
.ui-icon-unlocked { background-position: -208px -96px; }
.ui-icon-bookmark { background-position: -224px -96px; }
.ui-icon-tag { background-position: -240px -96px; }
.ui-icon-home { background-position: 0 -112px; }
.ui-icon-flag { background-position: -16px -112px; }
.ui-icon-calendar { background-position: -32px -112px; }
.ui-icon-cart { background-position: -48px -112px; }
.ui-icon-pencil { background-position: -64px -112px; }
.ui-icon-clock { background-position: -80px -112px; }
.ui-icon-disk { background-position: -96px -112px; }
.ui-icon-calculator { background-position: -112px -112px; }
.ui-icon-zoomin { background-position: -128px -112px; }
.ui-icon-zoomout { background-position: -144px -112px; }
.ui-icon-search { background-position: -160px -112px; }
.ui-icon-wrench { background-position: -176px -112px; }
.ui-icon-gear { background-position: -192px -112px; }
.ui-icon-heart { background-position: -208px -112px; }
.ui-icon-star { background-position: -224px -112px; }
.ui-icon-link { background-position: -240px -112px; }
.ui-icon-cancel { background-position: 0 -128px; }
.ui-icon-plus { background-position: -16px -128px; }
.ui-icon-plusthick { background-position: -32px -128px; }
.ui-icon-minus { background-position: -48px -128px; }
.ui-icon-minusthick { background-position: -64px -128px; }
.ui-icon-close { background-position: -80px -128px; }
.ui-icon-closethick { background-position: -96px -128px; }
.ui-icon-key { background-position: -112px -128px; }
.ui-icon-lightbulb { background-position: -128px -128px; }
.ui-icon-scissors { background-position: -144px -128px; }
.ui-icon-clipboard { background-position: -160px -128px; }
.ui-icon-copy { background-position: -176px -128px; }
.ui-icon-contact { background-position: -192px -128px; }
.ui-icon-image { background-position: -208px -128px; }
.ui-icon-video { background-position: -224px -128px; }
.ui-icon-script { background-position: -240px -128px; }
.ui-icon-alert { background-position: 0 -144px; }
.ui-icon-info { background-position: -16px -144px; }
.ui-icon-notice { background-position: -32px -144px; }
.ui-icon-help { background-position: -48px -144px; }
.ui-icon-check { background-position: -64px -144px; }
.ui-icon-bullet { background-position: -80px -144px; }
.ui-icon-radio-off { background-position: -96px -144px; }
.ui-icon-radio-on { background-position: -112px -144px; }
.ui-icon-pin-w { background-position: -128px -144px; }
.ui-icon-pin-s { background-position: -144px -144px; }
.ui-icon-play { background-position: 0 -160px; }
.ui-icon-pause { background-position: -16px -160px; }
.ui-icon-seek-next { background-position: -32px -160px; }
.ui-icon-seek-prev { background-position: -48px -160px; }
.ui-icon-seek-end { background-position: -64px -160px; }
.ui-icon-seek-start { background-position: -80px -160px; }
/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
.ui-icon-seek-first { background-position: -80px -160px; }
.ui-icon-stop { background-position: -96px -160px; }
.ui-icon-eject { background-position: -112px -160px; }
.ui-icon-volume-off { background-position: -128px -160px; }
.ui-icon-volume-on { background-position: -144px -160px; }
.ui-icon-power { background-position: 0 -176px; }
.ui-icon-signal-diag { background-position: -16px -176px; }
.ui-icon-signal { background-position: -32px -176px; }
.ui-icon-battery-0 { background-position: -48px -176px; }
.ui-icon-battery-1 { background-position: -64px -176px; }
.ui-icon-battery-2 { background-position: -80px -176px; }
.ui-icon-battery-3 { background-position: -96px -176px; }
.ui-icon-circle-plus { background-position: 0 -192px; }
.ui-icon-circle-minus { background-position: -16px -192px; }
.ui-icon-circle-close { background-position: -32px -192px; }
.ui-icon-circle-triangle-e { background-position: -48px -192px; }
.ui-icon-circle-triangle-s { background-position: -64px -192px; }
.ui-icon-circle-triangle-w { background-position: -80px -192px; }
.ui-icon-circle-triangle-n { background-position: -96px -192px; }
.ui-icon-circle-arrow-e { background-position: -112px -192px; }
.ui-icon-circle-arrow-s { background-position: -128px -192px; }
.ui-icon-circle-arrow-w { background-position: -144px -192px; }
.ui-icon-circle-arrow-n { background-position: -160px -192px; }
.ui-icon-circle-zoomin { background-position: -176px -192px; }
.ui-icon-circle-zoomout { background-position: -192px -192px; }
.ui-icon-circle-check { background-position: -208px -192px; }
.ui-icon-circlesmall-plus { background-position: 0 -208px; }
.ui-icon-circlesmall-minus { background-position: -16px -208px; }
.ui-icon-circlesmall-close { background-position: -32px -208px; }
.ui-icon-squaresmall-plus { background-position: -48px -208px; }
.ui-icon-squaresmall-minus { background-position: -64px -208px; }
.ui-icon-squaresmall-close { background-position: -80px -208px; }
.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
/* Misc visuals
----------------------------------*/
/* Corner radius */
.ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; -khtml-border-top-left-radius: 4px; border-top-left-radius: 4px; }
.ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; -khtml-border-top-right-radius: 4px; border-top-right-radius: 4px; }
.ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; -khtml-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; }
.ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br { -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; -khtml-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; }
/* Overlays */
.ui-widget-overlay { background: #666666 url(images/ui-bg_diagonals-thick_20_666666_40x40.png) 50% 50% repeat; opacity: .50;filter:Alpha(Opacity=50); }
.ui-widget-shadow { margin: -5px 0 0 -5px; padding: 5px; background: #000000 url(images/ui-bg_flat_10_000000_40x100.png) 50% 50% repeat-x; opacity: .20;filter:Alpha(Opacity=20); -moz-border-radius: 5px; -khtml-border-radius: 5px; -webkit-border-radius: 5px; border-radius: 5px; }/*
* jQuery UI Resizable 1.8.18
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Resizable#theming
*/
.ui-resizable { position: relative;}
.ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block; }
.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; }
.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; }
.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; }
.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; }
.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; }
.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; }
.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; }
.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; }
.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/*
* jQuery UI Dialog 1.8.18
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Dialog#theming
*/
.ui-dialog { position: absolute; padding: .2em; width: 300px; overflow: hidden; }
.ui-dialog .ui-dialog-titlebar { padding: .4em 1em; position: relative; }
.ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .1em 0; }
.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; }
.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; }
.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; }
.ui-dialog .ui-dialog-content { position: relative; border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; }
.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; }
.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { float: right; }
.ui-dialog .ui-dialog-buttonpane button { margin: .5em .4em .5em 0; cursor: pointer; }
.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; }
.ui-draggable .ui-dialog-titlebar { cursor: move; }

BIN
unmute.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 714 B