<!--{{{-->
<link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml' />
<!--}}}-->
Background: #fff
Foreground: #000
PrimaryPale: #8cf
PrimaryLight: #18f
PrimaryMid: #04b
PrimaryDark: #014
SecondaryPale: #ffc
SecondaryLight: #fe8
SecondaryMid: #db4
SecondaryDark: #841
TertiaryPale: #eee
TertiaryLight: #ccc
TertiaryMid: #999
TertiaryDark: #666
Error: #f88
/*{{{*/
body {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
 
a {color:[[ColorPalette::PrimaryMid]];}
a:hover {background-color:[[ColorPalette::PrimaryMid]]; color:[[ColorPalette::Background]];}
a img {border:0;}
 
h1,h2,h3,h4,h5,h6 {color:[[ColorPalette::SecondaryDark]]; background:transparent;}
h1 {border-bottom:2px solid [[ColorPalette::TertiaryLight]];}
h2,h3 {border-bottom:1px solid [[ColorPalette::TertiaryLight]];}
 
.button {color:[[ColorPalette::PrimaryDark]]; border:1px solid [[ColorPalette::Background]];}
.button:hover {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::SecondaryLight]]; border-color:[[ColorPalette::SecondaryMid]];}
.button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::SecondaryDark]];}
 
.header {background:[[ColorPalette::PrimaryMid]];}
.headerShadow {color:[[ColorPalette::Foreground]];}
.headerShadow a {font-weight:normal; color:[[ColorPalette::Foreground]];}
.headerForeground {color:[[ColorPalette::Background]];}
.headerForeground a {font-weight:normal; color:[[ColorPalette::PrimaryPale]];}
 
.tabSelected{color:[[ColorPalette::PrimaryDark]];
	background:[[ColorPalette::TertiaryPale]];
	border-left:1px solid [[ColorPalette::TertiaryLight]];
	border-top:1px solid [[ColorPalette::TertiaryLight]];
	border-right:1px solid [[ColorPalette::TertiaryLight]];
}
.tabUnselected {color:[[ColorPalette::Background]]; background:[[ColorPalette::TertiaryMid]];}
.tabContents {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::TertiaryPale]]; border:1px solid [[ColorPalette::TertiaryLight]];}
.tabContents .button {border:0;}
 
#sidebar {}
#sidebarOptions input {border:1px solid [[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel {background:[[ColorPalette::PrimaryPale]];}
#sidebarOptions .sliderPanel a {border:none;color:[[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel a:hover {color:[[ColorPalette::Background]]; background:[[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel a:active {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::Background]];}
 
.wizard {background:[[ColorPalette::PrimaryPale]]; border:1px solid [[ColorPalette::PrimaryMid]];}
.wizard h1 {color:[[ColorPalette::PrimaryDark]]; border:none;}
.wizard h2 {color:[[ColorPalette::Foreground]]; border:none;}
.wizardStep {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];
	border:1px solid [[ColorPalette::PrimaryMid]];}
.wizardStep.wizardStepDone {background:[[ColorPalette::TertiaryLight]];}
.wizardFooter {background:[[ColorPalette::PrimaryPale]];}
.wizardFooter .status {background:[[ColorPalette::PrimaryDark]]; color:[[ColorPalette::Background]];}
.wizard .button {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryLight]]; border: 1px solid;
	border-color:[[ColorPalette::SecondaryPale]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryPale]];}
.wizard .button:hover {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Background]];}
.wizard .button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::Foreground]]; border: 1px solid;
	border-color:[[ColorPalette::PrimaryDark]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryDark]];}
 
.wizard .notChanged {background:transparent;}
.wizard .changedLocally {background:#80ff80;}
.wizard .changedServer {background:#8080ff;}
.wizard .changedBoth {background:#ff8080;}
.wizard .notFound {background:#ffff80;}
.wizard .putToServer {background:#ff80ff;}
.wizard .gotFromServer {background:#80ffff;}
 
#messageArea {border:1px solid [[ColorPalette::SecondaryMid]]; background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]];}
#messageArea .button {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::SecondaryPale]]; border:none;}
 
.popupTiddler {background:[[ColorPalette::TertiaryPale]]; border:2px solid [[ColorPalette::TertiaryMid]];}
 
.popup {background:[[ColorPalette::TertiaryPale]]; color:[[ColorPalette::TertiaryDark]]; border-left:1px solid [[ColorPalette::TertiaryMid]]; border-top:1px solid [[ColorPalette::TertiaryMid]]; border-right:2px solid [[ColorPalette::TertiaryDark]]; border-bottom:2px solid [[ColorPalette::TertiaryDark]];}
.popup hr {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::PrimaryDark]]; border-bottom:1px;}
.popup li.disabled {color:[[ColorPalette::TertiaryMid]];}
.popup li a, .popup li a:visited {color:[[ColorPalette::Foreground]]; border: none;}
.popup li a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border: none;}
.popup li a:active {background:[[ColorPalette::SecondaryPale]]; color:[[ColorPalette::Foreground]]; border: none;}
.popupHighlight {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
.listBreak div {border-bottom:1px solid [[ColorPalette::TertiaryDark]];}
 
.tiddler .defaultCommand {font-weight:bold;}
 
.shadow .title {color:[[ColorPalette::TertiaryDark]];}
 
.title {color:[[ColorPalette::SecondaryDark]];}
.subtitle {color:[[ColorPalette::TertiaryDark]];}
 
.toolbar {color:[[ColorPalette::PrimaryMid]];}
.toolbar a {color:[[ColorPalette::TertiaryLight]];}
.selected .toolbar a {color:[[ColorPalette::TertiaryMid]];}
.selected .toolbar a:hover {color:[[ColorPalette::Foreground]];}
 
.tagging, .tagged {border:1px solid [[ColorPalette::TertiaryPale]]; background-color:[[ColorPalette::TertiaryPale]];}
.selected .tagging, .selected .tagged {background-color:[[ColorPalette::TertiaryLight]]; border:1px solid [[ColorPalette::TertiaryMid]];}
.tagging .listTitle, .tagged .listTitle {color:[[ColorPalette::PrimaryDark]];}
.tagging .button, .tagged .button {border:none;}
 
.footer {color:[[ColorPalette::TertiaryLight]];}
.selected .footer {color:[[ColorPalette::TertiaryMid]];}
 
.sparkline {background:[[ColorPalette::PrimaryPale]]; border:0;}
.sparktick {background:[[ColorPalette::PrimaryDark]];}
 
.error, .errorButton {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Error]];}
.warning {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryPale]];}
.lowlight {background:[[ColorPalette::TertiaryLight]];}
 
.zoomer {background:none; color:[[ColorPalette::TertiaryMid]]; border:3px solid [[ColorPalette::TertiaryMid]];}
 
.imageLink, #displayArea .imageLink {background:transparent;}
 
.annotation {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border:2px solid [[ColorPalette::SecondaryMid]];}
 
.viewer .listTitle {list-style-type:none; margin-left:-2em;}
.viewer .button {border:1px solid [[ColorPalette::SecondaryMid]];}
.viewer blockquote {border-left:3px solid [[ColorPalette::TertiaryDark]];}
 
.viewer table, table.twtable {border:2px solid [[ColorPalette::TertiaryDark]];}
.viewer th, .viewer thead td, .twtable th, .twtable thead td {background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::Background]];}
.viewer td, .viewer tr, .twtable td, .twtable tr {border:1px solid [[ColorPalette::TertiaryDark]];}
 
.viewer pre {border:1px solid [[ColorPalette::SecondaryLight]]; background:[[ColorPalette::SecondaryPale]];}
.viewer code {color:[[ColorPalette::SecondaryDark]];}
.viewer hr {border:0; border-top:dashed 1px [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::TertiaryDark]];}
 
.highlight, .marked {background:[[ColorPalette::SecondaryLight]];}
 
.editor input {border:1px solid [[ColorPalette::PrimaryMid]];}
.editor textarea {border:1px solid [[ColorPalette::PrimaryMid]]; width:100%;}
.editorFooter {color:[[ColorPalette::TertiaryMid]];}
 
#backstageArea {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::TertiaryMid]];}
#backstageArea a {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
#backstageArea a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; }
#backstageArea a.backstageSelTab {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
#backstageButton a {background:none; color:[[ColorPalette::Background]]; border:none;}
#backstageButton a:hover {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
#backstagePanel {background:[[ColorPalette::Background]]; border-color: [[ColorPalette::Background]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]];}
.backstagePanelFooter .button {border:none; color:[[ColorPalette::Background]];}
.backstagePanelFooter .button:hover {color:[[ColorPalette::Foreground]];}
#backstageCloak {background:[[ColorPalette::Foreground]]; opacity:0.6; filter:'alpha(opacity=60)';}
/*}}}*/
/*{{{*/
* html .tiddler {height:1%;}
 
body {font-size:.75em; font-family:arial,helvetica; margin:0; padding:0;}
 
h1,h2,h3,h4,h5,h6 {font-weight:bold; text-decoration:none;}
h1,h2,h3 {padding-bottom:1px; margin-top:1.2em;margin-bottom:0.3em;}
h4,h5,h6 {margin-top:1em;}
h1 {font-size:1.35em;}
h2 {font-size:1.25em;}
h3 {font-size:1.1em;}
h4 {font-size:1em;}
h5 {font-size:.9em;}
 
hr {height:1px;}
 
a {text-decoration:none;}
 
dt {font-weight:bold;}
 
ol {list-style-type:decimal;}
ol ol {list-style-type:lower-alpha;}
ol ol ol {list-style-type:lower-roman;}
ol ol ol ol {list-style-type:decimal;}
ol ol ol ol ol {list-style-type:lower-alpha;}
ol ol ol ol ol ol {list-style-type:lower-roman;}
ol ol ol ol ol ol ol {list-style-type:decimal;}
 
.txtOptionInput {width:11em;}
 
#contentWrapper .chkOptionInput {border:0;}
 
.externalLink {text-decoration:underline;}
 
.indent {margin-left:3em;}
.outdent {margin-left:3em; text-indent:-3em;}
code.escaped {white-space:nowrap;}
 
.tiddlyLinkExisting {font-weight:bold;}
.tiddlyLinkNonExisting {font-style:italic;}
 
/* the 'a' is required for IE, otherwise it renders the whole tiddler in bold */
a.tiddlyLinkNonExisting.shadow {font-weight:bold;}
 
#mainMenu .tiddlyLinkExisting,
	#mainMenu .tiddlyLinkNonExisting,
	#sidebarTabs .tiddlyLinkNonExisting {font-weight:normal; font-style:normal;}
#sidebarTabs .tiddlyLinkExisting {font-weight:bold; font-style:normal;}
 
.header {position:relative;}
.header a:hover {background:transparent;}
.headerShadow {position:relative; padding:4.5em 0 1em 1em; left:-1px; top:-1px;}
.headerForeground {position:absolute; padding:4.5em 0 1em 1em; left:0px; top:0px;}
 
.siteTitle {font-size:3em;}
.siteSubtitle {font-size:1.2em;}
 
#mainMenu {position:absolute; left:0; width:10em; text-align:right; line-height:1.6em; padding:1.5em 0.5em 0.5em 0.5em; font-size:1.1em;}
 
#sidebar {position:absolute; right:3px; width:16em; font-size:.9em;}
#sidebarOptions {padding-top:0.3em;}
#sidebarOptions a {margin:0 0.2em; padding:0.2em 0.3em; display:block;}
#sidebarOptions input {margin:0.4em 0.5em;}
#sidebarOptions .sliderPanel {margin-left:1em; padding:0.5em; font-size:.85em;}
#sidebarOptions .sliderPanel a {font-weight:bold; display:inline; padding:0;}
#sidebarOptions .sliderPanel input {margin:0 0 0.3em 0;}
#sidebarTabs .tabContents {width:15em; overflow:hidden;}
 
.wizard {padding:0.1em 1em 0 2em;}
.wizard h1 {font-size:2em; font-weight:bold; background:none; padding:0; margin:0.4em 0 0.2em;}
.wizard h2 {font-size:1.2em; font-weight:bold; background:none; padding:0; margin:0.4em 0 0.2em;}
.wizardStep {padding:1em 1em 1em 1em;}
.wizard .button {margin:0.5em 0 0; font-size:1.2em;}
.wizardFooter {padding:0.8em 0.4em 0.8em 0;}
.wizardFooter .status {padding:0 0.4em; margin-left:1em;}
.wizard .button {padding:0.1em 0.2em;}
 
#messageArea {position:fixed; top:2em; right:0; margin:0.5em; padding:0.5em; z-index:2000; _position:absolute;}
.messageToolbar {display:block; text-align:right; padding:0.2em;}
#messageArea a {text-decoration:underline;}
 
.tiddlerPopupButton {padding:0.2em;}
.popupTiddler {position: absolute; z-index:300; padding:1em; margin:0;}
 
.popup {position:absolute; z-index:300; font-size:.9em; padding:0; list-style:none; margin:0;}
.popup .popupMessage {padding:0.4em;}
.popup hr {display:block; height:1px; width:auto; padding:0; margin:0.2em 0;}
.popup li.disabled {padding:0.4em;}
.popup li a {display:block; padding:0.4em; font-weight:normal; cursor:pointer;}
.listBreak {font-size:1px; line-height:1px;}
.listBreak div {margin:2px 0;}
 
.tabset {padding:1em 0 0 0.5em;}
.tab {margin:0 0 0 0.25em; padding:2px;}
.tabContents {padding:0.5em;}
.tabContents ul, .tabContents ol {margin:0; padding:0;}
.txtMainTab .tabContents li {list-style:none;}
.tabContents li.listLink { margin-left:.75em;}
 
#contentWrapper {display:block;}
#splashScreen {display:none;}
 
#displayArea {margin:1em 17em 0 14em;}
 
.toolbar {text-align:right; font-size:.9em;}
 
.tiddler {padding:1em 1em 0;}
 
.missing .viewer,.missing .title {font-style:italic;}
 
.title {font-size:1.6em; font-weight:bold;}
 
.missing .subtitle {display:none;}
.subtitle {font-size:1.1em;}
 
.tiddler .button {padding:0.2em 0.4em;}
 
.tagging {margin:0.5em 0.5em 0.5em 0; float:left; display:none;}
.isTag .tagging {display:block;}
.tagged {margin:0.5em; float:right;}
.tagging, .tagged {font-size:0.9em; padding:0.25em;}
.tagging ul, .tagged ul {list-style:none; margin:0.25em; padding:0;}
.tagClear {clear:both;}
 
.footer {font-size:.9em;}
.footer li {display:inline;}
 
.annotation {padding:0.5em; margin:0.5em;}
 
* html .viewer pre {width:99%; padding:0 0 1em 0;}
.viewer {line-height:1.4em; padding-top:0.5em;}
.viewer .button {margin:0 0.25em; padding:0 0.25em;}
.viewer blockquote {line-height:1.5em; padding-left:0.8em;margin-left:2.5em;}
.viewer ul, .viewer ol {margin-left:0.5em; padding-left:1.5em;}
 
.viewer table, table.twtable {border-collapse:collapse; margin:0.8em 1.0em;}
.viewer th, .viewer td, .viewer tr,.viewer caption,.twtable th, .twtable td, .twtable tr,.twtable caption {padding:3px;}
table.listView {font-size:0.85em; margin:0.8em 1.0em;}
table.listView th, table.listView td, table.listView tr {padding:0px 3px 0px 3px;}
 
.viewer pre {padding:0.5em; margin-left:0.5em; font-size:1.2em; line-height:1.4em; overflow:auto;}
.viewer code {font-size:1.2em; line-height:1.4em;}
 
.editor {font-size:1.1em;}
.editor input, .editor textarea {display:block; width:100%; font:inherit;}
.editorFooter {padding:0.25em 0; font-size:.9em;}
.editorFooter .button {padding-top:0px; padding-bottom:0px;}
 
.fieldsetFix {border:0; padding:0; margin:1px 0px;}
 
.sparkline {line-height:1em;}
.sparktick {outline:0;}
 
.zoomer {font-size:1.1em; position:absolute; overflow:hidden;}
.zoomer div {padding:1em;}
 
* html #backstage {width:99%;}
* html #backstageArea {width:99%;}
#backstageArea {display:none; position:relative; overflow: hidden; z-index:150; padding:0.3em 0.5em;}
#backstageToolbar {position:relative;}
#backstageArea a {font-weight:bold; margin-left:0.5em; padding:0.3em 0.5em;}
#backstageButton {display:none; position:absolute; z-index:175; top:0; right:0;}
#backstageButton a {padding:0.1em 0.4em; margin:0.1em;}
#backstage {position:relative; width:100%; z-index:50;}
#backstagePanel {display:none; z-index:100; position:absolute; width:90%; margin-left:3em; padding:1em;}
.backstagePanelFooter {padding-top:0.2em; float:right;}
.backstagePanelFooter a {padding:0.2em 0.4em;}
#backstageCloak {display:none; z-index:20; position:absolute; width:100%; height:100px;}
 
.whenBackstage {display:none;}
.backstageVisible .whenBackstage {display:block;}
/*}}}*/
/***
StyleSheet for use when a translation requires any css style changes.
This StyleSheet can be used directly by languages such as Chinese, Japanese and Korean which need larger font sizes.
***/
/*{{{*/
body {font-size:0.8em;}
#sidebarOptions {font-size:1.05em;}
#sidebarOptions a {font-style:normal;}
#sidebarOptions .sliderPanel {font-size:0.95em;}
.subtitle {font-size:0.8em;}
.viewer table.listView {font-size:0.95em;}
/*}}}*/
/*{{{*/
@media print {
#mainMenu, #sidebar, #messageArea, .toolbar, #backstageButton, #backstageArea {display: none !important;}
#displayArea {margin: 1em 1em 0em;}
noscript {display:none;} /* Fixes a feature in Firefox 1.5.0.2 where print preview displays the noscript content */
}
/*}}}*/
<!--{{{-->
<div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'>
<div class='headerShadow'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
<div class='headerForeground'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
</div>
<div id='mainMenu' refresh='content' tiddler='MainMenu'></div>
<div id='sidebar'>
<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
</div>
<div id='displayArea'>
<div id='messageArea'></div>
<div id='tiddlerDisplay'></div>
</div>
<!--}}}-->
<!--{{{-->
<div class='toolbar' macro='toolbar [[ToolbarCommands::ViewToolbar]]'></div>
<div class='title' macro='view title'></div>
<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date'></span>)</div>
<div class='tagging' macro='tagging'></div>
<div class='tagged' macro='tags'></div>
<div class='viewer' macro='view text wikified'></div>
<div class='tagClear'></div>
<!--}}}-->
<!--{{{-->
<div class='toolbar' macro='toolbar [[ToolbarCommands::EditToolbar]]'></div>
<div class='title' macro='view title'></div>
<div class='editor' macro='edit title'></div>
<div macro='annotations'></div>
<div class='editor' macro='edit text'></div>
<div class='editor' macro='edit tags'></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser excludeLists'></span></div>
<!--}}}-->
To get started with this blank [[TiddlyWiki]], you'll need to modify the following tiddlers:
* [[SiteTitle]] & [[SiteSubtitle]]: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)
* [[MainMenu]]: The menu (usually on the left)
* [[DefaultTiddlers]]: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened
You'll also need to enter your username for signing your edits: <<option txtUserName>>
These [[InterfaceOptions]] for customising [[TiddlyWiki]] are saved in your browser
 
Your username for signing your edits. Write it as a [[WikiWord]] (eg [[JoeBloggs]])
 
<<option txtUserName>>
<<option chkSaveBackups>> [[SaveBackups]]
<<option chkAutoSave>> [[AutoSave]]
<<option chkRegExpSearch>> [[RegExpSearch]]
<<option chkCaseSensitiveSearch>> [[CaseSensitiveSearch]]
<<option chkAnimate>> [[EnableAnimations]]
 
----
Also see [[AdvancedOptions]]
<<importTiddlers>>
<<<
Flash Player cannot ship as part of a pure open source, or completely free operating system, as its distribution is bound to the Macromedia Licensing Program and subject to approval.
<<<
http://en.wikipedia.org/wiki/Adobe_Flash#Specifications
 
[[back|AntiFlash]]
<<<
In April 2006, the Flash SWF file format specification was released [...]. The file format specification document is offered only to developers who agree to a license agreement that permits them to use the specifications only to develop programs that can export to the Flash file format. The license forbids the use of the specifications to create programs that can be used for playback of Flash files. The Flash 9 specification was made available under similar restrictions.[27]
<<<
http://en.wikipedia.org/wiki/Adobe_Flash#Specifications
[[back|AntiFlash]]
In fact, now we've got lots of well argued reasons to hate Flash:
* Battery life sucks because of [[poor performance|Flash Performance]]
* Crashy
* Can't take advantage of hardware acceleration (except for video playback and some blitting operations)
* Big opaque ball of bits that can't be reused or modified without the source code
* Controlled by media interests (eg features to ensure adverts are watched)
* Flash breaks the web UI; browser navigation, text selection, accessibility
* [[Adobe and Open Specifications]]
* [[Adobe and Open Source]]
[[next|FlashSubstitutes]]
/***
|''Name:''|CecilyPlugin|
|''Description:''|A zooming user interface for TiddlyWiki|
|''Author:''|Jeremy Ruston (jeremy (at) osmosoft (dot) com)|
|''Source:''|http://svn.tiddlywiki.org/Trunk/contributors/JeremyRuston/plugins/CecilyPlugin.js|
|''CodeRepository:''|http://svn.tiddlywiki.org/Trunk/contributors/JeremyRuston/plugins/CecilyPlugin.js|
|''Version:''|0.1.2|
|''Status:''|Under Development|
|''Date:''|July 20, 2009|
|''Comments:''|Please make comments at http://groups.google.co.uk/group/TiddlyWikiDev|
|''License:''|BSD|
|''~CoreVersion:''|2.5.0|
***/
 
//{{{
// Ensure that the plugin is only installed once.
if(!version.extensions.CecilyPlugin) {
version.extensions.CecilyPlugin = {installed:true};
 
//-----------------------------------------------------------------------------------
// Geometry classes
//-----------------------------------------------------------------------------------
 
function interpolateLinear(t,a,b) {
	return a + (b - a) * t;
}
 
function interpolateQuad(t,a,b,c) {
	return Math.pow(1 - t,2) * a + 2 * t * (1 - t) * b + t * t * c;
}
 
// Point class {x:,y:}
function Point(x,y) {
	if(x instanceof Point) {
		this.x = x.x;
		this.y = x.y;
	} else {
		this.x = x;
		this.y = y;
	}
}
 
// Rectangle class {x:,y:,w:,h:} (w and h are both set to zero for empty rectangles)
function Rect(x,y,w,h) {
	if(x instanceof Rect) {
		this.x = x.x;
		this.y = x.y;
		this.w = x.w;
		this.h = x.h;
	} else {
		this.x = x ? x : 0;
		this.y = y ? y : 0;
		this.w = w ? w : 0;
		this.h = h ? h : 0;
	}
}
 
// Determines if this rectangle is empty
Rect.prototype.isEmpty = function() {
	return !this.w || !this.h;
}
 
// Returns the smallest rectangle that contains both this and the source rectangles
Rect.prototype.union = function(src) {
	if(this.isEmpty())
		return new Rect(src);
	if(src.isEmpty())
		return new Rect(this);
	var r = new Rect(Math.min(this.x,src.x),Math.min(this.y,src.y));
	r.w = Math.max(this.x+this.w-r.x,src.x+src.w-r.x);
	r.h = Math.max(this.y+this.h-r.y,src.y+src.h-r.y);
	return r;
}
 
// Determines if the source rectangle is completely contained within this rectangle
Rect.prototype.contains = function(src) {
	return (src.x > this.x) && ((this.x+this.w) > (src.x+src.w))
		&& (src.y > this.y) && ((this.y+this.h) > (src.y+src.h));
}
 
// Interpolates between this (t=0) and the source retangle (t=1)
Rect.prototype.interpolateLinear = function(t,src) {
	return new Rect(interpolateLinear(t,this.x,src.x), interpolateLinear(t,this.y,src.y),
		interpolateLinear(t,this.w,src.w), interpolateLinear(t,this.h,src.h));
}
 
// Interpolates between this (t=0) and the source rectangle (t=1) and a passing rectangle (t=0.5)
Rect.prototype.interpolateQuad = function(t,src,passing) {
	return new Rect(interpolateQuad(t,this.x,passing.x,src.x), interpolateQuad(t,this.y,passing.y,src.y),
					interpolateQuad(t,this.w,passing.w,src.w), interpolateQuad(t,this.h,passing.h,src.h));
}
 
// Scales a rectangle around it's centre
Rect.prototype.scale = function(scale) {
	var w = this.w * scale;
	var h = this.h * scale;
	return new Rect(this.x - (w-this.w)/2,this.y - (h-this.h)/2,w,h);
}
 
// Returns the midpoint of a rectangle
Rect.prototype.midPoint = function() {
	return new Point(this.x + this.w/2, this.y + this.h/2);
}
 
//-----------------------------------------------------------------------------------
// Generic DOM helper functions
//-----------------------------------------------------------------------------------
 
// Given a point in the coordinates of a target element, compute the coordinates relative to a specified base element
function normalisePoint(base,target,pt) {
	var e = target;
	var r = new Point(pt.x,pt.y);
	var parent = target.offsetParent;
	while(e && e !== base) {
		if(e == parent) {
			r.x += parent.offsetLeft;
			r.y += parent.offsetTop;
			parent = parent.offsetParent;
		}
		e = e.parentNode;
	}
	if(e == base)
		return r;
	else
		return null;
}
 
// Checks which of an array of classes are applied to a given element. Returns an array of the classes that are found
function hasClasses(e,classNames)
{
	var classes = e.className ? e.className.split(" ") : [];
	var results = [];
	for(var t=0; t<classNames.length; t++) {
		if(classes.indexOf(classNames[t]) != -1) {
			results.push(classNames[t]);
		}
	}
	return results;
}
 
//-----------------------------------------------------------------------------------
// Slider control
//-----------------------------------------------------------------------------------
 
// The slider control is constructed with a sliderInfo object that can contain the following keys:
//   place: DOM node to which the slider control is appended as a new child
//   min: Minimum value (integer)
//   max: Maximum value (integer)
//   getterTransform: function to convert internal slider values when reading them
//   setterTransform: function to convert to internal slider value when setting them
//   onChange: function(value) called when the slider moves
function SliderControl(sliderInfo) {
	merge(this,sliderInfo);
	if(!this.getterTransform)
		this.getterTransform = function(x) {return x;};
	if(!this.setterTransform)
		this.setterTransform = function(x) {return x;};
	this.slider = createTiddlyElement(this.place,"input");
	this.slider.type = "range";
	this.slider.min = this.min;
	this.slider.max = this.max;
	this.slider.style["-webkit-appearance"] = "slider-horizontal";
	var me = this;
	var handler = function (ev) {
		me.onChange(me.getterTransform(parseInt(me.slider.value,10)));
	};
	this.slider.oninput = handler;
	this.slider.onchange = handler;
}
 
SliderControl.prototype.set = function(value) {
	var n = this.setterTransform(value).toString();
	if(this.slider.value != n)
		this.slider.value = n;
};
 
//-----------------------------------------------------------------------------------
// cecilyTransform mechanism
//-----------------------------------------------------------------------------------
 
// Set up an element to be transformed
function cecilyTransform(element)
{
	addClass(element,"cecilyTransform");
	element.cecilyTransform = this;
	this.element = element;
	this.originalWidth = element.offsetWidth;
	this.bounds = new Rect(0,0,this.originalWidth,element.offsetHeight);
	this.rotate = 0;
	this.enlarge = 1;
}
 
// Applies any of these transformations over the top of prevailing ones
//   transforms.bounds = Rect() of bounds of element
//   transforms.rotate = numeric radian rotation applied to element around centre
//   transforms.enlarge = numeric scale factor applied after sizing
cecilyTransform.prototype.transform = function(transforms) {
	if(transforms.bounds !== undefined)
		this.bounds = new Rect(transforms.bounds);
	if(transforms.rotate !== undefined)
		this.rotate = transforms.rotate;
	if(transforms.enlarge !== undefined)
		this.enlarge = transforms.enlarge;
	var s = this.bounds.w / this.originalWidth;
	this.element.style[Cecily.cssTransform] =
			"translate(-50%,-50%) " +
			"scale(" + s + "," + s + ") " +
			"translate(50%,50%) " +
			"translate(" + this.bounds.x / s + "px," + this.bounds.y / s + "px) " +
			"rotate(" + this.rotate + "rad) " +
			"scale(" + this.enlarge + ")";
};
 
// Updates the bounds to account for text flow
cecilyTransform.prototype.getFlowedBounds = function() {
	this.bounds.h = this.element.offsetHeight * (this.bounds.w / this.element.offsetWidth);
	return new Rect(this.bounds);
};
 
//-----------------------------------------------------------------------------------
// cecilyViewer mechanism
//-----------------------------------------------------------------------------------
 
// Set up a zoomable viewer
//   frameElement - frame element that contains the zoomable sheet
//   sheetElement - child element of frame that is panned and zoomed within the frame
//	 backgroundName - name of background to use
function cecilyViewer(frameElement,sheetElement,backgroundName)
{
	addClass(frameElement,"cecilyViewer");
	frameElement.cecilyViewer = this;
	this.frameElement = frameElement;
	this.sheetElement = sheetElement;
	this.viewBounds = new Rect(0,0,100,100);
	this.createBackground();
	this.setBackground(backgroundName);
	this.drawBackground(this.viewBounds);
}
 
// Moves the viewport to accommodate the specified rectangle
cecilyViewer.prototype.setView = function(newBounds) {
	this.viewBounds = newBounds;
	var w = this.frameElement.offsetWidth;
	var h = this.frameElement.offsetHeight;
	var centre = newBounds.midPoint();
	this.viewBounds = new Rect(newBounds);
	if((w/h) > (newBounds.w/newBounds.h)) {
		this.viewBounds.w = newBounds.h * (w/h);
	} else {
		this.viewBounds.h = newBounds.w * (h/w);
	}
	this.viewBounds.x = centre.x - this.viewBounds.w/2;
	this.viewBounds.y = centre.y - this.viewBounds.h/2;
	var s = w/this.viewBounds.w;
	var transform = "scale(" + s + ") translate(" + -this.viewBounds.x + "px," + -this.viewBounds.y + "px)";
	this.sheetElement.style[Cecily.cssTransform] = transform;
	config.macros.cecilyZoom.propagate(s);
	this.drawBackground();
}
 
// Resize the viewer
cecilyViewer.prototype.onResize = function() {
	this.canvasElement.width = this.frameElement.offsetWidth;
	this.canvasElement.height = this.frameElement.offsetHeight;
}
 
// Set up a background canvas
cecilyViewer.prototype.createBackground = function()
{
	var w = this.frameElement.offsetWidth;
	var h = this.frameElement.offsetHeight;
	this.canvasElement = createTiddlyElement(null,"canvas",null,"cecilyCanvas");
	this.canvasElement.width = w;
	this.canvasElement.height = h;
	this.frameElement.insertBefore(this.canvasElement,this.frameElement.firstChild);
}
 
// Sets a new background
cecilyViewer.prototype.setBackground = function(backgroundName) {
	this.backgroundName = backgroundName;
	this.background = Cecily.backgrounds[backgroundName];
}
 
cecilyViewer.prototype.getBackground = function() {
	return this.backgroundName;
}
 
// Draws the current background
cecilyViewer.prototype.drawBackground = function() {
	if(this.background && this.background.drawBackground) {
		this.background.drawBackground(this.canvasElement,this.viewBounds);
	} else {
		var ctx = this.canvasElement.getContext('2d');
		ctx.fillStyle = "#eecccc";
		ctx.fillRect(0, 0, this.canvasElement.width, this.canvasElement.height);
	}
};
 
//-----------------------------------------------------------------------------------
// Zoom macro
//-----------------------------------------------------------------------------------
 
config.macros.cecilyZoom = {};
 
config.macros.cecilyZoom.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
	var zoomElem = createTiddlyElement(place,"span",null,"cecilyLabel cecilyZoom","zoom ");
	var me = this;
	zoomElem.sliderControl = new SliderControl({
		place: place,
		min: 0,
		max: 100,
		getterTransform: function(slider) {
			return Math.pow(Math.E,(slider/100)*12-6);
		},
		setterTransform: function(value) {
			var n = ((Math.log(value)+6)/12)*100;
			n = Math.min(100,Math.max(0,Math.floor(n + 0.5)));
			return n;
		},
		onChange: function(value) {
			if(cecily) {
				var w = cecily.frame.offsetWidth;
				var h = cecily.frame.offsetHeight;
				var cx = cecily.viewer.viewBounds.x + cecily.viewer.viewBounds.w/2;
				var cy = cecily.viewer.viewBounds.y + cecily.viewer.viewBounds.h/2;
				var newView = new Rect(0,0,w / value,h / value);
				newView.x = cx - newView.w/2;
				newView.y = cy - newView.h/2;
				cecily.viewer.setView(newView);
			}
		}
	});
}
 
config.macros.cecilyZoom.propagate = function(scale) {
	var zoomers = document.getElementsByClassName("cecilyZoom");
	for(var t = 0; t < zoomers.length; t++) {
		zoomers[t].sliderControl.set(scale);
	}
}
 
//-----------------------------------------------------------------------------------
// Zoom All macro
//-----------------------------------------------------------------------------------
 
config.macros.cecilyZoomAll = {};
 
config.macros.cecilyZoomAll.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
	createTiddlyButton(place,"zoom everything","Zoom out to see everything",function(ev) {
		if(cecily)
			cecily.scrollToAllTiddlers();
	});
}
 
//-----------------------------------------------------------------------------------
// Switch background macro
//-----------------------------------------------------------------------------------
 
config.macros.cecilyBackground = {
};
 
config.macros.cecilyBackground.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
	if(cecily) {
		createTiddlyElement(place,"span",null,"cecilyLabel","background ");
		var onchange = function(ev) {
			var sel = this.options[this.selectedIndex].value;
			if(sel != cecily.viewer.getBackground()) {
				cecily.setBackground(sel);
			}
		};
		var options = [];
		for(var t in Cecily.backgrounds) {
			options.push({name: t, caption: Cecily.backgrounds[t].title});
		}
		var d = createTiddlyDropDown(place,onchange,options,cecily.viewer.getBackground());
		addClass(d,"cecilyBackground");
	}
};
 
config.macros.cecilyBackground.propagate = function(background) {
	var backgrounders = document.getElementsByClassName("cecilyBackground");
	for(var k=0; k<backgrounders.length; k++) {
		var b = backgrounders[k];
		for(var s=0; s<b.options.length; s++) {
			if(b.options[s].value === background && b.selectedIndex !== s)
				b.selectedIndex = s;
		}
	}
};
 
//-----------------------------------------------------------------------------------
// Switch map macro
//-----------------------------------------------------------------------------------
 
config.macros.cecilyMap = {
};
 
config.macros.cecilyMap.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
	if(cecily) {
		createTiddlyElement(place,"span",null,"cecilyLabel","map ");
		var onchange = function(ev) {
			var sel = this.options[this.selectedIndex].value;
			if(sel != cecily.mapTitle) {
				cecily.setMap(sel);
			}
			cecily.scrollToAllTiddlers();
		};
		var options = [];
		var mapTiddlers = store.getTaggedTiddlers("cecilyMap")
		for(var t=0; t<mapTiddlers.length; t++) {
			options.push({name: mapTiddlers[t].title, caption: mapTiddlers[t].title});
		}
		var d = createTiddlyDropDown(place,onchange,options,cecily.mapTitle);
		addClass(d,"cecilyMap");
	}
};
 
config.macros.cecilyMap.propagate = function(map) {
	var mappers = document.getElementsByClassName("cecilyMap");
	for(var k=0; k<mappers.length; k++) {
		var m = mappers[k];
		for(var s=0; s<m.options.length; s++) {
			if(m.options[s].value === map && m.selectedIndex !== s)
				m.selectedIndex = s;
		}
	}
};
 
//-----------------------------------------------------------------------------------
// Cecily main class
//-----------------------------------------------------------------------------------
 
function Cecily()
{
	this.mapTitle = config.options.txtCecilyMap ? config.options.txtCecilyMap : "MyMap";
	this.drag = null;
	this.map = null;
}
 
Cecily.prototype.createDisplay = function() {
	this.overlayMenu = document.getElementById("overlayMenu");
	jQuery(this.overlayMenu).bind("mouseleave",this,this.onMouseLeaveOverlay);
	this.loadMap(this.mapTitle);
	this.container = document.getElementById(story.containerId());
	this.frame = this.container.parentNode;
	addClass(this.frame,"cecily");
	this.setViewSize();
	
	var background = config.options.txtCecilyBackground ? config.options.txtCecilyBackground : "plain";
	this.viewer = new cecilyViewer(this.frame,this.container,background);
	
	this.viewer.setView(new Rect(0,0,250,120));
	
	this.initScroller();
	var me = this;
	this.addEventHandler(window,"resize",this.onWindowResize,false);
	this.addEventHandler(window,"mousewheel",this.onMouseWheel,true);
	this.addEventHandler(document,"click",this.onMouseClickBubble,false);
	this.addEventHandler(document,"dblclick",this.onMouseDoubleClickBubble,false);
	this.addEventHandler(document,"mousedown",this.onMouseDownCapture,true);
	this.addEventHandler(document,"mousemove",this.onMouseMoveCapture,true);
	this.addEventHandler(document,"mouseup",this.onMouseUpCapture,true);
	this.defaultTiddler = null;
	window.setTimeout(function() {me.scrollToTiddler(me.defaultTiddler);},10);
}
 
Cecily.prototype.setViewSize = function() {
	var h = findWindowHeight();
	this.frame.style.height = h + "px";
}
 
Cecily.prototype.addEventHandler = function(element,type,handler,capture) {
	var me = this;
	element.addEventListener(type,function (ev) {
		// Safari has offsetX/Y
		// Firefox has layerX/Y
		if(ev.offsetX === undefined)
			ev.offsetX = ev.layerX;
		if(ev.offsetY === undefined)
			ev.offsetY = ev.layerY;
		if(ev.toElement === undefined)
			ev.toElement = ev.relatedTarget;
		return handler.call(me,ev);
		},capture);
}
 
Cecily.prototype.onWindowResize = function(ev) {
	this.setViewSize();
	this.viewer.onResize();
	this.viewer.drawBackground();
	return false;
}
 
Cecily.prototype.onMouseWheel = function(ev) {
	var newView = new Rect(this.viewer.viewBounds);
	newView.x -= (ev.wheelDeltaX/120) * (this.viewer.viewBounds.w/16);
	newView.y -= (ev.wheelDeltaY/120) * (this.viewer.viewBounds.w/16);
	this.viewer.setView(newView);
	return false;
};
 
Cecily.prototype.onMouseClickBubble = function(ev) {
	var tiddler = story.findContainingTiddler(ev.target);
	if(tiddler && this.drag === null && hasClasses(ev.target,["tiddlyLink","toolbar","title","tagged"]).length == 0) {
		// The next bit is equivalent to tiddler.parentNode.insertBefore(tiddler,null); but avoids moving
		// the element that was clicked on
		while(tiddler.nextSibling) {
			tiddler.parentNode.insertBefore(tiddler.nextSibling,tiddler);
		}
		this.scrollToTiddler(tiddler); 
	}
	return true;
};
 
Cecily.prototype.onMouseDoubleClickBubble = function(ev) {
	this.showOverlayMenu(new Point(ev.offsetX,ev.offsetY));
};
 
Cecily.prototype.onMouseDownCapture = function(ev) {
	for(var d=0; d<Cecily.draggerList.length; d++) {
		var dragger = Cecily.draggers[Cecily.draggerList[d]];
		if(dragger.isDrag(this,ev.target,ev)) {
			this.drag = {dragger: dragger};
			dragger.dragDown(this,ev.target,ev);
			break;
		}
	}
	if(this.drag !== null) {
		ev.stopPropagation();
		ev.preventDefault();
		return false;
	}
};
 
Cecily.prototype.onMouseMoveCapture = function(ev) {
	if(this.drag) {
		this.drag.dragger.dragMove(this,ev.target,ev);
		ev.stopPropagation();
		ev.preventDefault();
		return false;
	}
};
 
Cecily.prototype.onMouseUpCapture = function(ev) {
	if(this.drag) {
		this.drag.dragger.dragUp(this,ev.target,ev);
		this.drag = null;
		ev.stopPropagation();
		ev.preventDefault();
		return false;
	}
};
 
Cecily.draggers = {};
Cecily.draggerList = ["tiddlerDragger","tiddlerResizer","backgroundDragger"];
 
Cecily.draggers.tiddlerDragger = {
	isDrag: function(cecily,target,ev) {
		return hasClass(target,"toolbar") || hasClass(target,"title");
	},
	dragDown: function(cecily,target,ev) {
		var tiddler = story.findContainingTiddler(target);
		tiddler.parentNode.insertBefore(tiddler,null);
		cecily.drag.tiddler = tiddler;
		cecily.drag.tiddlerTitle = tiddler.getAttribute("tiddler");
		cecily.drag.lastPoint = new Point(ev.pageX,ev.pageY);
		addClass(tiddler,"drag");
	},
	dragMove: function(cecily,target,ev) {
		var dragThis = new Point(ev.pageX,ev.pageY);
		if(dragThis) {
			var s = cecily.frame.offsetWidth/cecily.viewer.viewBounds.w;
			var pos = new Rect(cecily.drag.tiddler.cecilyTransform.bounds.x + (dragThis.x - cecily.drag.lastPoint.x) / s,
								cecily.drag.tiddler.cecilyTransform.bounds.y + (dragThis.y - cecily.drag.lastPoint.y) / s,
								cecily.drag.tiddler.cecilyTransform.bounds.w, cecily.drag.tiddler.cecilyTransform.bounds.h);
			cecily.drag.tiddler.cecilyTransform.transform({bounds: pos});
			cecily.drag.lastPoint = dragThis;
		}
	},
	dragUp: function(cecily,target,ev) {
		removeClass(cecily.drag.tiddler,"drag");
		cecily.updateTiddlerPosition(cecily.drag.tiddlerTitle,cecily.drag.tiddler);
	}
};
 
Cecily.draggers.tiddlerResizer = {
	isDrag: function(cecily,target,ev) {
		return findRelated(target,"tagged","className","parentNode") !== null;
	},
	dragDown: function(cecily,target,ev) {
		var tiddler = story.findContainingTiddler(target);
		tiddler.parentNode.insertBefore(tiddler,null);
		cecily.drag.tiddler = tiddler;
		cecily.drag.tiddlerTitle = tiddler.getAttribute("tiddler");
		cecily.drag.startPoint = new Point(ev.pageX,ev.pageY);
		cecily.drag.startWidth = tiddler.cecilyTransform.bounds.w;
		addClass(tiddler,"drag");
	},
	dragMove: function(cecily,target,ev) {
		var s = cecily.frame.offsetWidth/cecily.viewer.viewBounds.w;
		var dragThis = new Point(ev.pageX,ev.pageY);
		if(dragThis) {
			var pos = new Rect(cecily.drag.tiddler.cecilyTransform.bounds);
			pos.w = cecily.drag.startWidth + (dragThis.x - cecily.drag.startPoint.x) / s;
			if(pos.w < 0.01)
				pos.w = 0.01;
			cecily.drag.tiddler.cecilyTransform.transform({bounds: pos});
		}
	},
	dragUp: function(cecily,target,ev) {
		removeClass(cecily.drag.tiddler,"drag");
		cecily.updateTiddlerPosition(cecily.drag.tiddlerTitle,cecily.drag.tiddler);
	}
};
 
Cecily.draggers.backgroundDragger = {
	isDrag: function(cecily,target,ev) {
		return target === cecily.viewer.canvasElement;
	},
	dragDown: function(cecily,target,ev) {
		cecily.drag.lastPoint = {x: ev.pageX, y: ev.pageY};
	},
	dragMove: function(cecily,target,ev) {
		var s = cecily.frame.offsetWidth/cecily.viewer.viewBounds.w;
		var newView = new Rect(cecily.viewer.viewBounds);
		newView.x -= (ev.pageX - cecily.drag.lastPoint.x)/s;
		newView.y -= (ev.pageY - cecily.drag.lastPoint.y)/s;
		cecily.drag.lastPoint = {x: ev.pageX, y: ev.pageY};
		cecily.viewer.setView(newView);
	},
	dragUp: function(cecily,target,ev) {
	}
};
 
Cecily.prototype.showOverlayMenu = function(pos)
{
	this.overlayMenu.style.display = "block";
	var overlayPos = new Rect(pos.x - this.overlayMenu.offsetWidth/2,pos.y - this.overlayMenu.offsetHeight/2,
							this.overlayMenu.offsetWidth,this.overlayMenu.offsetHeight);
	var w = this.frame.offsetWidth;
	var h = this.frame.offsetHeight;
	if(overlayPos.w > w || overlayPos.h > h) {
		overlayPos = overlayPos.scale(Math.min(w/overlayPos.w,h/overlayPos.h));
	}
	if(overlayPos.x < 0)
		overlayPos.x = 0;
	if(overlayPos.y < 0)
		overlayPos.y = 0;
	if(overlayPos.x + overlayPos.w > w)
		overlayPos.x = w - overlayPos.w;
	if(overlayPos.y + overlayPos.h > h)
		overlayPos.y = h - overlayPos.h;
	var scale = overlayPos.h / this.overlayMenu.offsetHeight;
	this.overlayMenu.style[Cecily.cssTransform] = "scale(" + scale + "," + scale + ")";
	this.overlayMenu.style.left = overlayPos.x + "px";
	this.overlayMenu.style.top = overlayPos.y + "px";
	this.overlayMenu.style.opacity = "0.9";
};
 
Cecily.prototype.onMouseLeaveOverlay = function(ev)
{
	var cecily = ev.data;
	jQuery(cecily.overlayMenu).css({
		'opacity' : '0.0',
		'display' : 'none'});
};
 
// Display a given tiddler with a given template. If the tiddler is already displayed but with a different
// template, it is switched to the specified template. If the tiddler does not exist, and if server hosting
// custom fields were provided, then an attempt is made to retrieve the tiddler from the server
// srcElement - reference to element from which this one is being opened -or-
//              special positions "top", "bottom"
// tiddler - tiddler or title of tiddler to display
// template - the name of the tiddler containing the template -or-
//            one of the constants DEFAULT_VIEW_TEMPLATE and DEFAULT_EDIT_TEMPLATE -or-
//            null or undefined to indicate the current template if there is one, DEFAULT_VIEW_TEMPLATE if not
// animate - whether to perform animations
// customFields - an optional list of name:"value" pairs to be assigned as tiddler fields (for edit templates)
// toggle - if true, causes the tiddler to be closed if it is already opened
Cecily.prototype.displayTiddler = function(superFunction,args) {
	var tiddler = args[1];
	var srcElement = args[0];
	args[0] = "bottom"; // srcElement to disable animation and scrolling
	var title = (tiddler instanceof Tiddler) ? tiddler.title : tiddler;
	var tiddlerElemBefore = story.getTiddler(title);
	superFunction.apply(story,args);
	var tiddlerElem = story.getTiddler(title);
	if(!tiddlerElem)
	 	return;
	var pos = this.getTiddlerPosition(title,srcElement);
	var transform = new cecilyTransform(tiddlerElem);
	transform.transform({bounds: pos});
	this.updateTiddlerPosition(title,tiddlerElem);
	if(!startingUp) {
		if(tiddlerElem.nextSibling) { // Move tiddler to the bottom of the Z-order if it's not already there
			tiddlerElem.parentNode.insertBefore(tiddlerElem,null);
		}
		this.scrollToTiddler(title);
	}
	this.defaultTiddler = tiddlerElem;
};
 
// Load the current map from a named tiddler
Cecily.prototype.loadMap = function(title) {
	this.map = {};
	var mapText = store.getTiddlerText(title,"");
    var positionRE = /^(\S+)\s(-?[0-9\.E]+)\s(-?[0-9\.E]+)\s(-?[0-9\.E]+)\s(-?[0-9\.E]+)$/mg;
    do {
        var match = positionRE.exec(mapText);
		if(match) {
			var title = decodeURIComponent(match[1]);
			this.map[title] = {
				x: parseFloat(match[2]),
				y: parseFloat(match[3]),
				w: parseFloat(match[4]),
				h: parseFloat(match[5])
			};
		}
	} while(match);
}
 
// Save the current map into a named tiddler
Cecily.prototype.saveMap = function(title) {
	var mapTiddler = store.getTiddler(title);
	if((mapTiddler == null) || (mapTiddler.isTagged("cecilyMap"))) {
		var text = [];
		for(var t in this.map) {
			var m = this.map[t];
			text.push(encodeURIComponent(t) + " " + Math.floor(m.x) + " " + Math.floor(m.y) + " " + Math.floor(m.w) + " " + Math.floor(m.h));
		}
		text.sort();
		store.saveTiddler(title,title,text.join("\n"),"Cecily");
		autoSaveChanges(null,[mapTiddler]);
	}
}
 
// Gets the Rect() position of a named tiddler
Cecily.prototype.getTiddlerPosition = function(title,srcElement) {
	var p = this.map[title];
	if(p)
		return new Rect(p.x,p.y,p.w,p.h);
	else {
		this.nextPos = this.nextPos ? this.nextPos + 250 : 250;
		return new Rect(this.nextPos,500,225,250);
	}
}
 
// Updates the position of a named tiddler into the current map
Cecily.prototype.updateTiddlerPosition = function(title,tiddlerElem) {
	this.map[title] = tiddlerElem.cecilyTransform.getFlowedBounds();
	this.saveMap(this.mapTitle);
}
 
// Switch to a new map
Cecily.prototype.setMap = function(title)
{
	this.mapTitle = title;
	config.options.txtCecilyMap = title;
	saveOptionCookie("txtCecilyMap");
	this.loadMap(title);
	var me = this;
	story.forEachTiddler(function(tiddler,elem) {
		var pos = me.getTiddlerPosition(tiddler);
		elem.cecilyTransform.transform({bounds: pos});
	});
	this.viewer.drawBackground();
	config.macros.cecilyMap.propagate(title);
}
 
Cecily.prototype.startHightlight = function(elem) {
	var me = this;
	var animationStart = new Date();
	var animationDuration = 3 * 1000;
	var highlight = {};
	var highlightElem = findRelated(elem.firstChild,"viewer","className","nextSibling");
	highlight.tick = function() {
		if(!highlightElem.parentNode)
			return false;
		var now = new Date();
		var t = (now - animationStart) / animationDuration;
		if(t < 1) {
			var p = (Math.sin(t*Math.PI*4 + Math.PI/2)+1)/2;
			highlightElem.style.backgroundColor = (new RGB("#ffff88")).mix(new RGB("#ffffff"),(p+1)/2).toString();
			return true;
		} else {
			highlightElem.style.backgroundColor = "";
			return false;
		}
	}
	if(highlightElem)
		anim.startAnimating(highlight);
};
 
Cecily.prototype.scrollToAllTiddlers = function() {
	var currRect = null;
	story.forEachTiddler(function (title,tiddlerElem) {
		var tiddlerRect = new Rect(tiddlerElem.cecilyTransform.getFlowedBounds());
		if(!currRect)
			currRect = tiddlerRect;
		else
			currRect = tiddlerRect.union(currRect);
	});
	if(currRect)
		this.startScroller([currRect.scale(1.2)]);
};
 
// Highlight a particular tiddler and scroll it into view
//  tiddler - title of tiddler or reference to tiddlers DOM element
Cecily.prototype.scrollToTiddler = function(tiddler) {
	var tiddlerElem = typeof tiddler == "string" ? story.getTiddler(tiddler) : tiddler;
	if(tiddlerElem) {
		this.startHightlight(tiddlerElem);
		var targetRect = new Rect(tiddlerElem.cecilyTransform.getFlowedBounds());
		if(this.viewer.viewBounds.contains(targetRect)) {
			this.startScroller([targetRect.scale(1.2)]);
		} else {
			var passingRect = this.viewer.viewBounds.union(targetRect);
			this.startScroller([passingRect.scale(1.1),targetRect.scale(1.2)]);
		}
	}
}
 
Cecily.prototype.initScroller = function() {
	var me = this;
	this.scroller = {
		scrolling: false
	};
	var s = this.scroller;
	me.scroller.tick = function() {
		var now = new Date();
		var t = (now - s.animationStart) / s.animationDuration;
		if(t > 1)
			t = 1;
		switch(s.rectList.length) {
			case 2:
				me.viewer.setView(s.rectList[0].interpolateLinear(t,s.rectList[1]));
				break;
			case 3:
				me.viewer.setView(s.rectList[0].interpolateQuad(t,s.rectList[2],s.rectList[1]));
				break;
		}
		if(t == 1) {
			s.scrolling = false;
			return false;
		} else
			return true;
	};
};
 
Cecily.prototype.startScroller = function(rectList,duration) { // One or more rectangles to scroll to in turn
	var s = this.scroller;
	s.rectList = [this.viewer.viewBounds];
	for(var r = 0; r < Math.min(rectList.length,2); r++)
		s.rectList.push(rectList[r]);
	s.animationStart = new Date();
	s.animationDuration = duration ? duration : 0.75 * 1000;
	s.currRect = 0;
	if(!s.scrolling) {
		s.scrolling = true;
		anim.startAnimating(s);
	}
};
 
Cecily.prototype.setBackground = function(background) {
	config.options.txtCecilyBackground = background;
	saveOptionCookie("txtCecilyBackground");
	this.viewer.setBackground(background);
	this.viewer.drawBackground();
	config.macros.cecilyBackground.propagate(background);
};
 
//-----------------------------------------------------------------------------------
// Background plumbing and generators
//-----------------------------------------------------------------------------------
 
Cecily.backgrounds = {};
 
Cecily.backgrounds.plain = {
		title: "Plain",
		description: "Plain",
		drawBackground: function(canvas,view) {
			var w = canvas.width;
			var h = canvas.height;
			var ctx = canvas.getContext('2d');
			ctx.fillStyle = "#aaaacc";
			ctx.fillRect(0, 0, w, h);
		}
};
 
Cecily.backgrounds.fractal = {
		title: "Fractal",
		description: "Fractal cracks",
		drawBackground: function(canvas,view) {
			var w = canvas.width;
			var h = canvas.height;
			var scale = w/view.w;
			var ctx = canvas.getContext('2d');
			ctx.fillStyle = "#cc8888";
			ctx.fillRect(0, 0, w, h);
			var Turtle = function Turtle(x,y,direction) {
				this.x = x ? x : 0;
				this.y = y ? y : 0;
				this.direction = direction ? direction : 0;
			};
			Turtle.prototype.line = function(d) {
					this.x += Math.sin(this.direction) * d;
					this.y -= Math.cos(this.direction) * d;
				};
			Turtle.prototype.turn = function(a) {
					this.direction += a;
				};
			// Gosper curve as a series of angles to turn (in degrees anti clockwise, for humans)
			var fractalPath =  [0,300,240,60,120,0,60]; // [0,-60,60,-240,240];
			// Work out the overall angle and length of the curve
			var turtle = new Turtle(0,0,0);
			for(var t=0; t<fractalPath.length; t++) {
				turtle.turn(fractalPath[t] / 180 * Math.PI);
				turtle.line(1);
			}
			var fractalAngle = Math.atan2(turtle.y,turtle.x);
			var fractalLength = Math.sqrt(Math.pow(turtle.x,2)+Math.pow(turtle.y,2));
			// Recursive function to draw a generation of the curve
			var drawLeg = function drawLeg(p1,p2,depth) {
				// Work out the angle and length required
				var legLength = Math.sqrt(Math.pow(p2.x-p1.x,2)+Math.pow(p2.y-p1.y,2));
				var legAngle = Math.atan2(p2.y-p1.y,p2.x-p1.x);
				// Initialise the turtle
				var legScale = legLength / fractalLength;
				var turtle = new Turtle(p1.x,p1.y,legAngle);
				turtle.turn(-fractalAngle);
				// Step through the curve
				for(var t=0; t<fractalPath.length; t++) {
					var prevX = turtle.x;
					var prevY = turtle.y;
					turtle.turn(fractalPath[t] / 180 * Math.PI);
					turtle.line(legScale);
					if(depth > 0)
						drawLeg(new Point(prevX,prevY),new Point(turtle.x,turtle.y),depth - 1);
					ctx.lineTo(turtle.x,turtle.y);
				}
			}
			var drawCircle = function(x,y,r) {
				var radgrad = ctx.createRadialGradient(x,y,r,x-r/3,y-r/3,1);
				radgrad.addColorStop(0, '#8888cc');
				radgrad.addColorStop(0.9, '#f0f0ff');
				radgrad.addColorStop(1, '#ffffff');
				ctx.fillStyle = radgrad;
				ctx.beginPath();
				ctx.arc(x,y,r,0,2*Math.PI,0);
				ctx.fill();
			}
			var scale = w/view.w;
			// Get the position of the canvas on the plane
			var px = view.x + view.w/2 - (w/2) / scale;
			var py = view.y + view.h/2 - (h/2) / scale;
			var pw = w / scale;
			var ph = h / scale;
			// Map coordinates
			var p1 = new Point(-430,11);
			var p2 = new Point(1530,674);
			var x = 100;
			var y = 100;
			var r = 500;
			// To 0..1,0..1 for viewport
			p1.x = (p1.x - px)/pw;
			p1.y = (p1.y - py)/ph;
			p2.x = (p2.x - px)/pw;
			p2.y = (p2.y - py)/ph;
			
			x = (x - px)/pw;
			y = (y - py)/ph;
			r = r / pw;
			// To x,y for canvas
			x = x * w;
			y = y * h;
			r = r * w;
			
			p1.x = p1.x * w;
			p1.y = p1.y * h;
			p2.x = p2.x * w;
			p2.y = p2.y * h;
			
			// Draw the circle
			drawCircle(x,y,r);
			// Draw the curve
			ctx.strokeStyle = "#0ff";
			ctx.lineWidth = 1;
			ctx.beginPath();
			ctx.moveTo(p1.x,p1.y);
			drawLeg(p1,p2,3);
			ctx.stroke();
			// Draw the curve
			ctx.strokeStyle = "#F00";
			ctx.lineWidth = 1;
			ctx.beginPath();
			ctx.moveTo(p1.x,p1.y);
			drawLeg(p1,p2,2);
			ctx.stroke();
			// Draw the curve
			ctx.strokeStyle = "#Ff0";
			ctx.lineWidth = 1;
			ctx.beginPath();
			ctx.moveTo(p1.x,p1.y);
			drawLeg(p1,p2,1);
			ctx.stroke();
			// Draw the curve
			ctx.strokeStyle = "#F0f";
			ctx.lineWidth = 1;
			ctx.beginPath();
			ctx.moveTo(p1.x,p1.y);
			drawLeg(p1,p2,0);
			ctx.stroke();
		}
};
 
Cecily.backgrounds.experimental = {
		title: "Experimental",
		description: "Experimental scratchpad",
		drawBackground: function(canvas,view) {
			var w = canvas.width;
			var h = canvas.height;
			var ctx = canvas.getContext('2d');
			ctx.fillStyle = "#cccccc";
			ctx.fillRect(0, 0, w, h);
			var drawCircle = function(x,y,r) {
				var radgrad = ctx.createRadialGradient(x,y,r,x-r/3,y-r/3,1);
				radgrad.addColorStop(0, '#8888cc');
				radgrad.addColorStop(0.9, '#f0f0ff');
				radgrad.addColorStop(1, '#ffffff');
				ctx.fillStyle = radgrad;
				ctx.beginPath();
				ctx.arc(x,y,r,0,2*Math.PI,0);
				ctx.fill();
			}
			var scale = w/view.w;
			var px = view.x + view.w/2 - (w/2) / scale;
			var py = view.y + view.h/2 - (h/2) / scale;
			var pw = w / scale;
			var ph = h / scale;
			// Map coordinates
			var x = 100;
			var y = 100;
			var r = 500;
			// To 0..1,0..1 for viewport
			x = (x - px)/pw;
			y = (y - py)/ph;
			r = r / pw;
			// To x,y for canvas
			x = x * w;
			y = y * h;
			r = r * w;
			drawCircle(x,y,r);
		}
};
 
Cecily.backgrounds.honeycomb = {
		title: "Honeycomb",
		description: "Honeycomb balls",
		drawBackground: function(canvas,view) {
			var w = canvas.width;
			var h = canvas.height;
			var scale = w/view.w;
			var t = ((Math.log(scale)+6)/12);
			t = Math.max(t,0);
			t = Math.min(t,1);
			var ctx = canvas.getContext('2d');
			ctx.fillStyle = "#eeeecc";
			ctx.fillRect(0, 0, w, h);
			var drawCircle = function(x,y,r,c) {
				ctx.fillStyle = c ? c : '#88ff88';
				ctx.beginPath();
				ctx.arc(x,y,r,0,2*Math.PI,0);
				ctx.fill();
			}
			var modulo = function(num,denom) {
				return num-Math.floor(num/denom)*denom;
			}
			var gapX = 200 * scale;
			var yscale = Math.sin(Math.PI/3)*2;
			var gapY = gapX * yscale;
			var radius = 60 * scale;
			if(gapX < 15) {
				gapX = 15;
				gapY = 15;
			}
			if(radius < 7) {
				radius = 7;
			}
			
			for(var y = -modulo(view.y * scale,gapY) - gapY; y < h + gapY; y += gapY) {
				for(var x = -modulo(view.x * scale,gapX) - gapX; x < w + gapX; x += gapX) {
					drawCircle(x,y,radius,"#ddddbb");
					drawCircle(x + gapX/2,y + gapY/2,radius,"#ddddbb");
					/*
					drawCircle(x,y,radius/2,"#555577");
					drawCircle(x + gapX/4,y + gapY/4,radius/2,"#555577");
					drawCircle(x + gapX/2,y,radius/2,"#555577");
					drawCircle(x + gapX/4,y - gapY/4,radius/2,"#555577");
					drawCircle(x - gapX/4,y + gapY/4,radius/2,"#555577");
					drawCircle(x - gapX/2,y,radius/2,"#555577");
					drawCircle(x - gapX/4,y - gapY/4,radius/2,"#555577");
					drawCircle(x + gapX/2,y + gapY/2,radius/2,"#555577");
					drawCircle(x + gapX,y + gapY/2,radius/2,"#555577");
					*/
				}
			}
		}
};
 
//-----------------------------------------------------------------------------------
// Utilities for class substitution
//-----------------------------------------------------------------------------------
 
function overrideMethod(instance,method,override)
{
	var oldFunction = instance[method];
	instance[method] = function () {return override(oldFunction,arguments);};
}
 
//-----------------------------------------------------------------------------------
// Initialisation code (executed during loading of plugin)
//-----------------------------------------------------------------------------------
 
function runCecily()
{
	setStylesheet(store.getRecursiveTiddlerText(tiddler.title + "##StyleSheet"),"cecily");
	window.cecily = new Cecily();
	overrideMethod(story,"displayTiddler",function(superFunction,arguments) {cecily.displayTiddler(superFunction,arguments);});
	store.addNotification("PageTemplate",function () {cecily.createDisplay();});
}
 
Cecily.cssTransform = null;
if(document.body.style['-webkit-transform'] !== undefined)
	Cecily.cssTransform = '-webkit-transform';
if(document.body.style['MozTransform'] !== undefined)
	Cecily.cssTransform = 'MozTransform';
 
if(Cecily.cssTransform) {
	runCecily();
} else {
	alert("ProjectCecily currently only works on Safari 3.1, Firefox 3.1 and Google Chrome. Use the WebKit nightly build from http://webkit.org/ for the best experience");
}
 
} // if(!version.extensions.CecilyPlugin)
 
/***
!StyleSheet
 
body {
	font-family: helvetica,arial;
}
 
#displayArea.cecily {
	float: none;
	margin: 0em 0em 0em 0em;
	position: relative;
	background-color: #ffff88;
	overflow: hidden;
}
 
div#messageArea {
	-webkit-transition: opacity 0.3s ease-in-out;
	-webkit-border-radius: 4px;
	-moz-border-radius: 4px;
	border: 1px solid #222;
	background-color: [[ColorPalette::SecondaryLight]];
	background-image: -webkit-gradient(linear, left top, left bottom, from([[ColorPalette::SecondaryPale]]), to([[ColorPalette::SecondaryDark]]), color-stop(0.1,[[ColorPalette::SecondaryLight]]), color-stop(0.6,[[ColorPalette::SecondaryMid]]));
	opacity: 0.8;
}
 
div#messageArea:hover {
	opacity: 1.0;
}
 
div#messageArea .button {
	padding: 0 0.25em 0 0.25em;
	text-decoration: none;
	-webkit-transition: opacity 0.3s ease-in-out;
	opacity: 0;
	-webkit-border-radius: 3px;
	-moz-border-radius: 3px;
	background-color: #aaa;
	background: -webkit-gradient(linear, left top, left bottom, from([[ColorPalette::PrimaryLight]]), to([[ColorPalette::PrimaryDark]]), color-stop(0.5,[[ColorPalette::PrimaryMid]]));
	color: [[ColorPalette::TertiaryPale]];
}
 
div#messageArea:hover .button {
	opacity: 1;
}
 
div#messageArea:hover .button:active {
	background-color: [[ColorPalette::Foreground]];
	color: [[ColorPalette::Background]];
}
 
#overlayMenu {
	-webkit-box-shadow: 2px 2px 13px #000;
	-moz-box-shadow: 2px 2px 13px #000;
	-webkit-transition: opacity 0.2s ease-in-out;
	z-index: 100;
	position: absolute;
	padding: 0.1em 0.1em 0.1em 0.1em;
	font-size: 0.8em;
	-webkit-border-radius: 4px;
	-moz-border-radius: 4px;
	border: 1px solid #666;
	background-color: #bbb;
	background-image: -webkit-gradient(linear, left top, left bottom, from(#999), to(#ddd), color-stop(0.3,#bbb));
	opacity: 0;
	display: none;
}
 
#overlayMenu table.twtable {
	border: none;
}
 
#overlayMenu .twtable th{
	border: none;
}
 
#overlayMenu .twtable td {
	border: none;
}
 
#overlayMenu .twtable tr {
	border: none;
	border-bottom: 1px solid #ccc;
}
 
#overlayMenu a {
	-webkit-transition: color 0.3s ease-in-out;
	text-decoration: none;
	font-weight: bold;
	font-style: normal;
	color: #000;
	background-color: #999;
	border: none;
	margin: 0 0.25em 0 0.25em;
	padding: 3px 3px 3px 3px;
	-webkit-border-radius: 3px;
	-moz-border-radius: 3px;
}
 
#overlayMenu a:hover {
	text-decoration: none;
	font-weight: bold;
	font-style: normal;
	color: #000;
	background-color: #ff0;
	border: none;
}
 
#overlayMenu .overlayCommand {
	font-size: 2em;
	color: #fff;
	text-shadow: #000 2px 2px 3px;
}
 
div#backstageArea {
	position: absolute;
}
 
.cecilyCanvas {
	position: absolute;
	left: 0px;
	top: 0px;
	background-color: #eee;
}
 
#tiddlerDisplay {
	position: relative;
	-webkit-transform-origin: 0% 0%;
	-moz-transform-origin: 0% 0%;
}
 
.cecily .tiddler {
	position: absolute;
	top: 0px;
	left: 0px;
	width: 360px;
	padding: 0;
	background-color: #fff;
	overflow: hidden;
	border: 1px solid black;
}
 
.cecily .tiddler.drag {
	-webkit-box-shadow: 2px 2px 13px #000;
	-moz-box-shadow: 2px 2px 13px #000;
}
 
.cecily .tiddler .heading {
	background-color: #bbb;
	background-image: -webkit-gradient(linear, left top, left bottom,
		from(#fff), color-stop(0.5,#bbb), color-stop(0.51,#aaa), to(#999));
}
 
.cecily .tiddler .toolbar {
	cursor: all-scroll;
	padding: 4pt 2pt 4pt 4pt;
	color: #aaa;
}
 
.cecily .tiddler.selected .toolbar {
	color: #fff;
}
 
.cecily .tiddler .toolbar a {
	-webkit-transition: opacity 0.3s ease-in-out;
	opacity: 0;
	margin: 0 0.25em 0 0.25em;
	border: none;
	-webkit-border-radius: 3px;
	-moz-border-radius: 3px;
}
 
.cecily .tiddler.selected .toolbar a {
	opacity: 1;
	background-color: #aaa;
	background: -webkit-gradient(linear, left top, left bottom, from(#888), to(#ccc), color-stop(0.5,#aaa), color-stop(0.7,#bbb));
	color: #fff;
}
 
.cecily .tiddler.selected .toolbar a:hover {
	background-color: #c80;
	background-image: -webkit-gradient(linear, left top, left bottom, from(#c80), to(#fc1), color-stop(0.5,#c80));
	color: #000;
}
 
.cecily .tiddler.selected .toolbar a:active {
	background-color: [[ColorPalette::Foreground]];
	background-image: none;
	color: [[ColorPalette::Background]];
}
 
.cecily .tiddler .title {
	cursor: all-scroll;
	padding: 2pt 8pt 2pt 8pt;
	color: #000;
	background-color: transparent;
	text-shadow: #fff 1px 1px 2px;
}
 
.cecily .tiddler .subtitle {
	padding: 2pt 8pt 4pt 8pt;
	color: #444;
	font-size: 0.6em;
}
 
.cecily .tiddler .viewer {
	padding: 4pt 8pt 4pt 8pt;
	background-color: #fff;
}
 
.cecily .tiddler .tagging, .cecily .tiddler .tagged {
	float: none;
	border: none;
	padding: 2pt 8pt 2pt 8pt;
	background-image: -webkit-gradient(linear, left bottom, left top, from(#888), to(#ccc), color-stop(0.5,#ccc), color-stop(0.95,#fff));
	margin: auto;
}
 
.cecily .tiddler .tagged {
	cursor: nwse-resize;
}
 
.cecily .tiddler.selected .tagging, .cecily .tiddler.selected .tagged {
	background-color: auto;
	border: auto;
}
 
.cecilyButton {
	-webkit-appearance: push-button;
}
 
!(end of StyleSheet)
 
***/
[img[http://osmosoft-resources.tiddlyspace.com/chromeexperiments.png]]
http://www.chromeexperiments.com/
 
[[back|FlashSubstitutes]]
MainMenu
! Flash player is now the device driver layer for the web
 
Is that true? How did we get here? What's going to happen next?
 
[[next|Pre-History]]
<<<
In certain cases Flash writes double nots in your code. Consider ActionScript code {{{if (a<=b) { ... } else { ... } }}} Two inversions are created here by Flash compiler:
 
{{{
push 'b'
getVariable
push 'a'
getVariable
lessThan // a>b?
not // now inverted: a<=b?
not // prepare for branch to the else condition: again a>b?
branchIfTrue elseCondition
}}}
 
As you see, Flash is not very flexible compiling your statements and does not change the order of operands in expression or use another pattern for if statement. It doesn't really make sense. The only purpose here could be an attempt to force type conversion to boolean. The next action you always see in the code, however, is branchIfTrue. And this action does type conversion itself.
So Flasm will automatically remove those nots in update mode.
<<<
http://flasm.sourceforge.net/
 
[[back|AntiFlash]]
[img[http://osmosoft-resources.tiddlyspace.com/amazon.png]]
 
[[next|WhenCanIuse]]
Several of sites celebrating the obsolescence of Flash:
 
* ChromeExperiments
* [[I Can't Believe It's Not Flash]]
 
[[jQuery]] can smooth out the differences between browsers
 
[[next|DeviceDriverLayer]]
[>img[http://osmosoft-resources.tiddlyspace.com/msnclassic.jpg]]
August 1996, Microsoft adopted FutureSplash for MSN, their alternative to the Internet. Even though it came from a 6 person company. Shortly afterwards, they sold to Macromedia, and Macromedia Flash 1.0 was born.
 
[[next|FlashDominance]]
[img[http://osmosoft-resources.tiddlyspace.com/icantbelieve.png]]
http://icantbelieveitsnotflash.com/
 
[[back|FlashSubstitutes]]
Flash has been an object of derision for over a decade:
[img[http://osmosoft-resources.tiddlyspace.com/Flash99percentBad.png]]
 
http://www.useit.com/alertbox/20001029.html
 
[[next|ReallyBad]]
One of the original creators of FutureSplash, the forerunner of [[Flash]]
[>img[http://osmosoft-resources.tiddlyspace.com/jonathan_gay.jpg]]
 
http://www.adobe.com/macromedia/events/john_gay/index.html
 
[[next|Pre-History]]
[[Introduction]]
[[Pre-History]]
[[MidHistory]]
[[History]]
[[FlashDominance]]
[[WhenCanIuse]]
[[Why I Develop with Flash Player]]
[[NextSteps]]
[>img[http://osmosoft-resources.tiddlyspace.com/celanim.gif]]
In 1995, extended SmartSketch into animation. Then created a Java applet so that those animations could be played in Web browsers. Horrible and slow, it wasn't until late 1995 that Netscape released their plugin API, and hence an efficient version. Renamed FutureSplash Animator.
 
[[next|History]]
Adobe%20and%20Open%20Source -791 1567 225 139
Adobe%20and%20Open%20Specifications -804 1329 226 208
AntiFlash -543 1196 389 352
ChromeExperiments -186 1567 225 203
DeviceDriverLayer -105 912 417 251
Flash%20Performance -508 1590 225 394
Flash%20and%20Hardware%20Acceleration 1084 676 225 91
FlashDominance 466 756 424 384
FlashSubstitutes -118 1203 531 340
FlashSupreme 801 673 832 634
FlashToday 774 336 424 107
History 381 336 382 400
I%20Can't%20Believe%20It's%20Not%20Flash 149 1564 225 226
Introduction -427 480 279 322
Jonathan%20Gay -96 147 125 165
MainMenu -289 331 174 99
MidHistory 133 336 225 309
MyMap 1250 500 225 117
NextSteps 550 1572 1459 491
Pre-History -100 335 225 519
ReallyBad -724 815 574 355
SideBarTabs -986 186 132 171
ViewTemplate 500 500 225 159
WhenCanIuse 888 150 452 568
Why%20I%20Develop%20with%20Flash%20Player 920 753 723 768
jQuery -23 1789 225 205
! Authoring Tools!
|{{overlayCommand{go}}} |[[home|ProjectCecily]][[a-z|SideBarTabs]]<<search>> |
|{{overlayCommand{zoom}}} |<<cecilyZoomAll>><<cecilyZoom>> |
|{{overlayCommand{map}}} |<<cecilyMap>><<closeAll>> |
|{{overlayCommand{share}}} |<<permaview>> |
|{{overlayCommand{create}}} |<<saveChanges>><<newTiddler>><<newJournal "DD MMM YYYY" "journal">> |
|{{overlayCommand{tweak}}} |<<cecilyBackground>>[[options|OptionsPanel]] |
<!--{{{-->
<div id='mainMenu' refresh='content' tiddler='MainMenu'></div>
<div id='sidebar'>
<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
</div>
<div id='displayArea'>
<div id='overlayMenu' refresh='content' tiddler='OverlayMenu' style='display: none'></div>
<div id='messageArea'></div>
<div id='tiddlerDisplay'></div>
</div>
<!--}}}-->
[[Jonathan Gay]] started FutureWave Software in January 1993, to build graphics editors for the sexy new pen computers of the time. They developed SmartSketch for the PenPoint platform from [[GO|http://en.wikipedia.org/wiki/GO_Corp.]], a competitor to Microsoft and Apple in pen computing operating systems.
 
[>img[http://osmosoft-resources.tiddlyspace.com/go_tablet.jpg]]GO was bought in January 1994 by AT&T, the makers of the specialised Hobbit processor that they used. In the same month, AT&T decided to cancel the Hobbit processor, and therefore closed down GO, leaving FutureWave without a market.
 
<<<
The failure of Go and pen computing was a big setback for us. The only opportunity we saw was to take our software and make it run on Windows and the Macintosh. We did it, but now we were competing against Illustrator and FreeHand. It was a struggle.
<<<
[[next|MidHistory]]
Almost as hated as PowerPoint, in fact
<<<
Unfortunately, many Flash designers decrease the granularity of user control and revert to presentation styles that resemble television rather than interactive media
<<<
http://www.useit.com/alertbox/20001029.html
[[next|AntiFlash]]
img {
width: 340px;
}
<!--{{{-->
<div class='heading'>
<div class='toolbar' macro='toolbar [[ToolbarCommands::ViewToolbar]]'></div>
<div class='title' macro='view title'></div>
</div>
<div class='viewer' macro='view text wikified'></div>
<div class='tagged' macro='tags'></div>
<div class='tagClear'></div>
<!--}}}-->
Lots of good HTML 5 resources
 
[img[http://osmosoft-resources.tiddlyspace.com/whencaniusetop.png]]
[img[http://osmosoft-resources.tiddlyspace.com/whencaniusebottom.png]]
 
http://a.deveria.com/caniuse/
 
[[but|Why I Develop with Flash Player]]
[img[http://osmosoft-resources.tiddlyspace.com/whyidevelopwithflashplayer.png]]
 
http://twitter.com/jesterxl/status/5530952482
 
[[next|NextSteps]]
[img[http://osmosoft-resources.tiddlyspace.com/jquery.png]]
http://jquery.com/
 
[[back|FlashSubstitutes]]