Update (06 - 06 - 2005 11:13pm) :Updated source with a few changes. Helps to alleviate the problem with white space and text-wrapping. There are a few more options now, including inserting spaces around braces, parentheses, and insertting spaces after commas. These changes help to take care of most, if not all, text-wrapping issues for most source code.
I finally got around to completing my widget. I worked for a while on trying to create the editing area as a contentEditable DIV, but finally gave up. There just isn't enough documentation on it. I got it work, but the scrolling didn't happen automatically like it does with the TEXTAREA, so I gave up, and broke Apple's user interface guidelines for widgets. Oh well, at least it functions.
When you create a widget for the Dashboard, there are a few files that must be created. These are the following:
Widget HTML - defines the layout using HTML
Widget CSS - defines the properties and stylistic elements
Widget Javascript - defines the behavior of the widget
Info.plist - widget bundle properties
Widget Background - image that comprises the body of the widget
Widget Preferences Background - image that comprises the body of the widget's preferences
Widget Icon - image for the icon in the widget tray of the dashboard
My widget, as you know if you are a regular reader, converts source code for inclusion in HTML markup. Its pretty simplistic right now, but functional enough for my needs. Currently, it supports converting spaces to non-breaking spaces, converting tabs to a variable number of non-breaking spaces, converting newlines to breaks, and converts the <, >, and & characters.
Ok, enough babbling. Here's the source for the files that make up this widget. Explanation of the source will follow in a future post.
"Code2HTML.html"
<html>
<head>
<!-- The style sheet should be kept in a separate file; it contains the design for the widget -->
<style type="text/css">
@import "Code2HTML.css";
</style>
<!-- The JavaScript file contains the logic needed for this widget -->
<script type='text/javascript' src='Code2HTML.js' charset='utf-8'></script>
<script type='text/javascript' src='file:///System/Library/WidgetResources/button/genericButton.js' charset='utf-8'></script>
</head>
<body onload='setup ( ) ;'>
<div id="front" onmousemove='mousemove ( event ) ;' onmouseout='mouseexit ( event ) ;'>
<!-- front side here -->
<img src='Default.png'>
<textarea wrap="off" id="codeField"></textarea>
<div class='flip' id='flip' onclick='showBack ( event ) ;' onmouseover='enterflip ( event ) ;' onmouseout='exitflip ( event ) ';></div>
<div class="convertButton" id="convertButtonDiv"></div>
<div class="revertButton" id="revertButtonDiv"></div>
<div class='flip' id='fliprollie'></div>
<!-- end front side -->
</div>
<div id="back">
<!-- reverse side here -->
<img src='Back.png'>
<input type="checkbox" id="convertNewlines" />
<div id="convertNewlinesLabel">Convert Newlines To Breaks</div>
<input type="checkbox" id="convertTabs" checked="checked"/>
<div id="convertTabsLabel">Convert Tabs To</div>
<input type="text" id="tabSize" value="4" size="2" maxlength="2" /><div id="tabSizeLabel">Spaces</div>
<input type="checkbox" id="convertSpaces" checked="checked" /><div id="convertSpacesLabel">Convert Spaces To Non-Breaking Spaces</div>
<input type="checkbox" id="convertParens" checked="checked"/>
<div id="convertParensLabel">Insert Spaces Around Parentheses</div>
<input type="checkbox" id="convertBraces" checked="checked"/>
<div id="convertBracesLabel">Insert Spaces Around Braces</div>
<input type="checkbox" id="convertCommas" checked="checked"/>
<div id="convertCommasLabel">Insert Spaces After Commas</div>
<div class="doneButton" id="doneButtonDiv"></div>
<!-- end reverse side -->
</div>
</body>
</html>
"Code2HTML.css"
body {
margin: 0;
font-family: verdana;
font-size: 9pt;
}
.convertButton {
position: absolute;
bottom: 28px;
left: 180px;
}
.revertButton {
position: absolute;
bottom: 28px;
left: 180px;
display: none;
}
#codeField {
padding: 3px;
position: absolute;
top : 40px;
left: 40px;
width: 340px;
height: 330px;
}
#convertNewlines {
position: absolute;
top: 50px;
left: 30px;
}
#convertTabs {
position: absolute;
top: 100px;
left: 30px;
}
#convertSpaces {
position: absolute;
top: 150px;
left: 30px;
}
#tabSize {
position: absolute;
top: 95px;
left: 150px;
}
#tabSizeLabel {
position: absolute;
top: 100px;
left: 180px;
}
#convertTabsLabel {
position: absolute;
top: 100px;
left: 50px;
}
#convertSpacesLabel {
position: absolute;
top: 150px;
left: 50px;
}
#convertNewlinesLabel {
position: absolute;
top: 50px;
left: 50px;
}
#convertParens {
position: absolute;
top: 180px;
left: 30px;
}
#convertParensLabel {
position: absolute;
top: 180px;
left: 50px;
}
#convertBraces {
position: absolute;
top: 210px;
left: 30px;
}
#convertBracesLabel {
position: absolute;
top: 210px;
left: 50px;
}
#convertCommas {
position: absolute;
top: 240px;
left: 30px;
}
#convertCommasLabel {
position: absolute;
top: 240px;
left: 50px;
}
.flip {
position:absolute;
bottom:30px;
right:30px;
width:13px;
height:13px;
}
#flip {
opacity:0;
background:url ( file:///System/Library/WidgetResources/ibutton/white_i.png ) no-repeat top left;
z-index:8000;
}
#fliprollie {
display:none;
opacity:0.25;
background:url ( file:///System/Library/WidgetResources/ibutton/white_rollie.png ) no-repeat top left;
z-index:7999;
}
#back {
display:none;
}
.doneButton {
position:absolute;
bottom:30px;
left:30px;
}
"Code2HTML.js"
var code = "";
function setup ( )
{
createGenericButton ( document.getElementById ( 'convertButtonDiv' ) ,
"Convert", convert ) ;
createGenericButton ( document.getElementById ( 'revertButtonDiv' ) ,
"Revert", revert ) ;
createGenericButton ( document.getElementById ( 'doneButtonDiv' ) ,
"Done", hideBack ) ;
loadPrefs ( ) ;
return 0;
}
function loadPrefs ( )
{
if ( window.widget )
{
widget.setPreferenceForKey (
document.getElementById ( 'tabSize' ) .value,
"tabSize" ) ;
widget.setPreferenceForKey (
document.getElementById ( 'convertTabs' ) .checked,
"convertTabs" ) ;
widget.setPreferenceForKey (
document.getElementById ( 'convertNewlines' ) .checked,
"convertNewlines" ) ;
widget.setPreferenceForKey (
document.getElementById ( 'convertSpaces' ) .checked,
"convertSpaces" ) ;
widget.setPreferenceForKey (
document.getElementById ( 'convertParens' ) .checked,
"convertParens" ) ;
widget.setPreferenceForKey (
document.getElementById ( 'convertBraces' ) .checked,
"convertBraces" ) ;
widget.setPreferenceForKey (
document.getElementById ( 'convertCommas' ) .checked,
"convertCommas" ) ;
}
}
function savePrefs ( )
{
if ( window.widget )
{
document.getElementById ( 'tabSize' ) .value =
widget.preferenceForKey (
"tabSize" ) ;
document.getElementById ( 'convertTabs' ) .checked,
widget.preferenceForKey (
"convertTabs" ) ;
document.getElementById ( 'convertNewlines' ) .checked,
widget.preferenceForKey (
"convertNewlines" ) ;
document.getElementById ( 'convertSpaces' ) .checked,
widget.preferenceForKey (
"convertSpaces" ) ;
document.getElementById ( 'convertParens' ) .checked,
widget.preferenceForKey (
"convertParens" ) ;
document.getElementById ( 'convertBracees' ) .checked,
widget.preferenceForKey (
"convertBraces" ) ;
document.getElementById ( 'convertCommas' ) .checked,
widget.preferenceForKey (
"convertCommas" ) ;
}
}
function revert ( )
{
document.getElementById ( 'codeField' ) .value = code;
document.getElementById ( 'codeField' ) .readOnly = false;
document.getElementById ( 'revertButtonDiv' ) .style.display = "none";
document.getElementById ( 'convertButtonDiv' ) .style.display = "block";
}
function convert ( )
{
var html;
code = document.getElementById ( 'codeField' ) .value;
html = code.replace ( /&/g, "&" ) ;
html = html.replace ( /</g, "<" ) ;
html = html.replace ( />/g, ">" ) ;
if ( document.getElementById ( 'convertSpaces' ) .checked )
{
html = html.replace ( /\ \ /g, " " ) ;
html = html.replace ( /\ \ /g, " " ) ;
}
if ( document.getElementById ( 'convertCommas' ) .checked )
{
html = html.replace ( /\, /g, ", " ) ;
}
if ( document.getElementById ( 'convertParens' ) .checked )
{
html = html.replace ( /\ ( /g, " ( " ) ;
html = html.replace ( /\ ) /g, " ) " ) ;
}
if ( document.getElementById ( 'convertBraces' ) .checked )
{
html = html.replace ( /\ { /g, " { " ) ;
html = html.replace ( /\ } /g, " } " ) ;
}
if ( document.getElementById ( 'convertTabs' ) .checked )
{
var tabSpaces = "";
for ( var i = 0; i < document.getElementById ( 'tabSize' ) .value; i++ )
tabSpaces += " ";
html = html.replace ( /\t/g, tabSpaces ) ;
}
if ( document.getElementById ( 'convertNewlines' ) .checked )
html = html.replace ( /\n/g, "<br/>\n" ) ;
document.getElementById ( 'codeField' ) .value = html;
document.getElementById ( 'convertButtonDiv' ) .style.display = "none";
document.getElementById ( 'revertButtonDiv' ) .style.display = "block";
document.getElementById ( 'codeField' ) .readOnly = true;
}
function showBack ( )
{
var front = document.getElementById ( "front" ) ;
var back = document.getElementById ( "back" ) ;
if ( window.widget )
widget.prepareForTransition ( "ToBack" ) ;
front.style.display="none";
back.style.display="block";
if ( window.widget )
setTimeout ( 'widget.performTransition ( ) ;', 0 ) ;
}
function hideBack ( )
{
var front = document.getElementById ( "front" ) ;
var back = document.getElementById ( "back" ) ;
if ( window.widget )
widget.prepareForTransition ( "ToFront" ) ;
back.style.display="none";
front.style.display="block";
if ( window.widget )
setTimeout ( 'widget.performTransition ( ) ;', 0 ) ;
savePrefs ( ) ;
}
var flipShown = false;
var animation = { duration:0, starttime:0, to:1.0, now:0.0, from:0.0, firstElement:null, timer:null } ;
function mousemove ( event )
{
if ( !flipShown )
{
if ( animation.timer != null )
{
clearInterval ( animation.timer ) ;
animation.timer = null;
}
var starttime = ( new Date ) .getTime ( ) - 13;
animation.duration = 500;
animation.starttime = starttime;
animation.firstElement = document.getElementById ( 'flip' ) ;
animation.timer = setInterval ( "animate ( ) ;", 13 ) ;
animation.from = animation.now;
animation.to = 1.0;
animate ( ) ;
flipShown = true;
}
}
function mouseexit ( event )
{
if ( flipShown )
{
// fade in the info button
if ( animation.timer != null )
{
clearInterval ( animation.timer ) ;
animation.timer = null;
}
var starttime = ( new Date ) .getTime ( ) - 13;
animation.duration = 500;
animation.starttime = starttime;
animation.firstElement = document.getElementById ( 'flip' ) ;
animation.timer = setInterval ( "animate ( ) ;", 13 ) ;
animation.from = animation.now;
animation.to = 0.0;
animate ( ) ;
flipShown = false;
}
}
function animate ( )
{
var T;
var ease;
var time = ( new Date ) .getTime ( ) ;
T = limit_3 ( time-animation.starttime, 0, animation.duration ) ;
if ( T >= animation.duration )
{
clearInterval ( animation.timer ) ;
animation.timer = null;
animation.now = animation.to;
}
else
{
ease = 0.5 - ( 0.5 * Math.cos ( Math.PI * T / animation.duration ) ) ;
animation.now = computeNextFloat ( animation.from, animation.to, ease ) ;
}
animation.firstElement.style.opacity = animation.now;
}
function limit_3 ( a, b, c )
{
return a < b ? b : ( a > c ? c : a ) ;
}
function computeNextFloat ( from, to, ease )
{
return from + ( to - from ) * ease;
}
function enterflip ( event )
{
document.getElementById ( 'fliprollie' ) .style.display = 'block';
}
function exitflip ( event )
{
document.getElementById ( 'fliprollie' ) .style.display = 'none';
}
"Info.plist"
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDisplayName</key>
<string>Code2HTML</string>
<key>CFBundleIdentifier</key>
<string>net.4haks.code2html</string>
<key>CFBundleName</key>
<string>Code2HTML</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>CloseBoxInsetX</key>
<integer>5</integer>
<key>CloseBoxInsetY</key>
<integer>5</integer>
<key>MainHTML</key>
<string>Code2HTML.html</string>
</dict>
</plist>
That's it for now. I'll try and go into some of the more interesting details of this code in a future post.
Saturday, June 04, 2005
Code2HTML Widget
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment