Saturday, November 24, 2007

Load A New File From Template In Notepad++

What are needed:
  1. htmlTemplate.htm: Your html template
  2. nppNewHtml.bat: a .bat file looking like this:
copy  d:\html\htmlTemplate.htm  d:\html\new.htm
"C:\Program Files\Notepad++\notepad++.exe"  d:\html\new.htm

Save nppNewHtml.bat in, for example, d:\html.

In Notepad++, go to menu [Run], or hit [F5].

In the input box shown under the title [Type your program to run here], enter:

d:\html\nppNewHtml.bat


Hit [Save] to save it to a run menu item (give it a name like "new html").

From now on, you can go to the [Run] menu and find an item called "new html" that allows you to open a new html file based on your template.

Monday, July 23, 2007

Javascript string replacing function (2)

Previously I mentioned a string replacing function that, instead of building a string this way:

tag= '<' + tagname + ' style="background-color:'
+ bgcolor + ';color:' + color + '">' + text + '</' + tagname +'>'

allows for doing it this way:

tag0= '<___ style="background-color:___;color:___">___</___>'
tag1 = tag0._(tagname, bgcolor, color, text, tagname)

or this way :

tag00= '<[.tagname.] style="background-color:[.bgcolor.];'+
'color:[.color.]">[.txt.]</[.tagname.]>'

data = {tagname:'span', bgcolor:'#EEFFBB',color:'red',txt:'tag2'}
tag2 = tag00._(data)


After playing with the regular expression for a while, I come up with a short version :

String.prototype._=function(){
var i=-1, a= arguments
var t= typeof(a[0])=='string'
var rx = t?(/___/g):(/\[\.[A-Za-z0-9_]*\.\]/g)
var f = t?function(x){i++;return a[i]}
:function(x){return a[0][x.slice(2, -2)]||''}
return this.replace(rx,f)
}


To me it's amazing to see how such a task can be accomplished with such a neat code.

Saturday, July 21, 2007

Problems in Javascript Subclassing

In javascript, the subclassing is done this way:
[1] function Person(name, sex){    # Define the superclass
[2] this.name = name
[3] this.sex = sex
[4] this.prn = function(){ return this.name + ', '+ this.sex }
[5] }

[6] function Man(name){ this.sex = 'M' } # Define the subclass
[7] Man.prototype = new Person() # Connect Person and Man

And the instantiation:
[8]  p = new Person('Jane','F')
[9] m = new Man('John')

Checking the function prn (defined in [4]):
p.prn() ==> Jane, F
m.prn() ==> undefined, M


As shown, the name John given to Man when instantiating m ([9]) is NOT transferred to m. It is obvious 'cos we didn't instruct it to do so when defining Man in [6].

Problem.1: Subclass Arguments Are Not Automatically Transferred To Subclass Instances



So, unless explicitly instructed, the arguments of a subclass will not be assigned to the instance of that subclass. That means, [6] needs to be re-defined as:
function Man(name){ 
this.sex = 'M'
this.name= name # <== new line
}

Note that the above highlighted line already exists in the superclass (line [2]). Why does it have to be re-done again in the subclass ? Didn't we already instruct Person to be the super class ?

Problem.2: An Instance of Subclass Is an Instance of the Superclass too ?


Lets investigate the instanceof operator in javascript:
[10] (p instanceof Person)  ==> true
[11] (m instanceof Man) ==> true
[12] (m instanceof Person) ==> true


Line [10] and [11] are what we expect. But [12]? How come m is an instance of Person ? This doesn't sound right.

Problem.3: The Constructor of A Subclass' Instance Is The Superclass But Not the Subclass ?


Check the constructor of the instances of subclass and of superclass:
[13] getConstructor(p) ==> Person
[14] getConstructor(m) ==> Person

Shouldn't the constructor of m be Man?

Solution Proposal



Lets try a new way of defining a subclass. Instead of
function Man(name){ 
this.sex = 'M'
this.name= name # <== new line
}

We try this:
function Woman(name){ update(this, new Person(name, 'F'))}
w= new Woman("Jan")

See the following table for comparison:

ClassesInstanceinstanceof
Person
constructor
function Person(name, sex){
this.name = name
this.sex = sex
this.prn = function(){
return this.name +','+this.sex}
}
ptruePerson
function Man(name){
this.sex = 'M'
this.name= name }
Man.prototype = new Person
mtruePerson
function Woman(name){
update(this, new Person(name,'F'))
}
wfalseWoman

Friday, July 20, 2007

About Subclassing in Javascript

Let the superclass be:
function Person(sex){alert('I am Person')}

and subclasses:

function Man(){this.sex='M'}
function Woman(){this.sex='F'}

The following two subclassing approaches don't seem to make any difference:

Man.prototype = new Person
Woman.prototype = new Person()
m = new Man()
w = new Woman()

In both cases:
  • The function Person will be executed
  • The constructor of both m and w are Person


Wednesday, April 04, 2007

IE's TextRange.compareEndPoints()

TextRange object

The other day I was coding a web page that needs to make use of some javascript functions of textarea element. I did a little dig-out and ran into this compareEndPoints function. It is a function attached to a TextRange object. The IE-specific TextRange object serves as a mediator for the text input element (<input> with type="text", <textarea>...) to manupilate text-related operations (search, replace, copy, paste, insert ...).

Now assuming we have a <textarea> element object called oTextArea, a TextRange object can be created in one of the following ways:
  1. var oTR1 = oTextArea.createTextRange()
  2. var oTR2 = document.selection.createRange()
These code should appear inside an eventhandler, for example:

  oTextArea.onselect = function(){
...
var oTR1= this.createTextRange()
var oTR2= document.selection.createRange()
...
}

Both oTR1 and oTR2 are a TextRange object that has the following properties:

PropertiesoTR1oTR2
boundingHeight1616
boundingLeft15141
boundingTop124124
boundingWidth25256
offsetLeft15141
offsetTop124124
textThis is the test
<textarea> element
textarea
htmlTextThis is the test
&lt;textarea&gt; element.
textarea

Note that:
  1. What createTextRange returns (=oTR1) covers the entire value range of <textarea> while createRange covers only the range of text that is selected;
  2. All the numerical property values in TextRange object are in pixel. No information of index (the start and end point for the selected text range) is available;

compareEndPoints (type, textRange)

I won't go through all the methods of the TextRange object but this one compareEndPoints(type, textRange). This function checks the relationship between two TextRange objects by comparing the starting and ending indices of textRange.text :

  textRange1.compareEndPoints("StartToStart", textRange2)

The argument type can be one of the fillowing 4 : "StartToStart", "StartToEnd", "EndToStart" and "EndToEnd". When it is "StartToStart", it compares the starting index of textRange1.text with the that of textRange2.text. The return is either -1, 0 or 1.

Now, the reason that I talk about this compareEndPoints specifically. Since it is used upon two TextRange objects, and as we saw earlier that both createRange() and createTextRange() return a TextRange object, instinctly it would work like this:

   var oTR1= oTextArea.createTextRange()
var oTR2= document.selection.createRange()
var x = oTR1.compareEndPoints("StartToStart", oTR2)

However, it won't work. IE rejects this usage by showing "illegal argument" error. It took me a while of searching and code digging to figure out a way to do this:

   var oTR1 = document.body.createTextRange(); // Create TextRange on entire body 
oTR1.moveToElementText(oTextArea) // Use moreToElementText to move
// the TextRange to the textarea

var oTR2= document.selection.createRange();
var x = oTR1.compareEndPoints("StartToStart", oTR2)

Only when the createTextRange( ) is used in this way can the resulting TextRange object be used together in compareEndPoints( ) with that generated by createRange( ).

Saturday, March 31, 2007

My Javascript addEvent

In the end of 2005 several web programming gurus, Peter-Paul Koch (ppk), Dean Edward and Scott Andrew LePera host a "javascript recoding contest" in which they asked participants to submit their code for adding and removing events in javascript. A template page was designed to test the code. The winner was John Resig, whose original code is here. The final code was slightly modified version:

function addEvent( obj, type, fn )
{
if (obj.addEventListener)
obj.addEventListener( type, fn, false );
else if (obj.attachEvent)
{
obj["e"+type+fn] = fn;
obj[type+fn] = function() { obj["e"+type+fn]( window.event ); }
obj.attachEvent( "on"+type, obj[type+fn] );
}
}
function removeEvent( obj, type, fn )
{
if (obj.removeEventListener)
obj.removeEventListener( type, fn, false );
else if (obj.detachEvent)
{
obj.detachEvent( "on"+type, obj[type+fn] );
obj[type+fn] = null;
obj["e"+type+fn] = null;
}
}

I personally don't feel comfortable with these code. First of all, as one of the comments mentioned (#2 Posted by Tino Zijdel on 18 October 2005) that in cases of multiple-eventhandlers in IE, the eventhandler added last will be executed first:

... since this is for IE a wrapper around attachEvent, the execution order of the events is different ('random' according to Microsofts documentation, but LIFO in practice against FIFO using the W3C event model)

A simple test reveals this problem:

function init(){
var btn=document.createElement('input')
btn.value = 'Click me'
btn.type = 'button'
document.body.appendChild( btn )
addEvent( btn, "click", function(){alert("#1")} )
addEvent( btn, "click", function(){alert("#2")} )
}

Run the init() function as the onload eventhandler (<body onload="init()">) and load with browser. Firefox alerts "#1" then "#2". But IE alerts "#2" first then "#1", which is not what it should be.

Another part making me uncomfortable is that it uses the entire function content as the key name of a hash:

   obj["e"+type+fn] = fn;
obj[type+fn] = function() { obj["e"+type+fn]( window.event ); }

Although none obvioiusly problem is mentioned or discussed, it could result in extremely long and complex key name, which, IMHO, is not what a hash is supposed to be.

I therefore came up with my own version:

function addEvent( obj, type, fn ) 
{
obj.eventHooks = obj.eventHooks || {} // set .eventHooks for obj if not exists
var evs = obj.eventHooks[type] || [] // set .eventHooks[type] if not exists
obj.eventHooks[type] = evs.concat( fn.concat? fn:[fn]) // this allows for multiple fns added
obj['on'+type] = function (e) // like addEvent(obj,"click",[f1,f2])
{
for (var i=0;i<this.eventHooks[type].length;i++)
{
this.tmp = this.eventHooks[type][i] // attach to the obj such that the this
this.tmp(e) // inside the function points to the obj correctly
this.tmp = undefined
}
}
obj = null
}
function removeEvent( obj, type, fn )
{
if (obj.eventHooks && obj.eventHooks[type])
{
var evs = obj.eventHooks[type]
for (var i=0; i<evs.length;i++)
{
if (evs[i]==fn)
{
obj.eventHooks[type] = evs.slice(0,i).concat(evs.slice(i+1))
break
}
}
}
obj = null
}

Unlike most addEvent inventions, in which either addEventListener or attachEvent is used, the above code simply stores eventhandlers into a buffer that has the following structure:

  obj.eventHooks = { click    : [f1, f2]
, keyup : []
, mouseover: [f3]
....
}

For each event, say, click, a function is assigned to the .onclick. This function carries out the following steps:
  1. Check if obj.eventHooks exists. If not, create one
  2. Check if obj.eventHooks.click exists. If not, create one
  3. Loop through each functions in obj.eventHooks.click
  4. For each function, attaches it to obj as a temporary obj.tmp, then executes obj.tmp(e). This makes sure that any this keyword inside that function point to the obj correctly.
A test page, using the contest template, is here. I am sure that there will be some downside along with this code, but at least at this moment it seems to solve the problems came with the winning code.

Tuesday, March 20, 2007

Javascript string replacing function

In python there's a very convinient string replacing function that comes with every string. The following example show how it works:

To construct a string:   "firstname:Runsun, lastname:Pan, project:wysiwyc"
firstname='Runsun'
lastname ='Pan'
project ='wysiwyc'
s= 'firstname:<b>%s</b>, lastname:<b>%s</b>, project:<b>%s</b>'%(firstname, lastname, project)

The symbol % attaching to the end of a string is a function to insert a series of strings (firstname, lastname, project) into the original string. This not only makes it easier to read but also easier to type, as compared to:

'firstname:<b>'+ firstname +'</b>, lastname:<b>'+ lastname+ '</b>, project:<b>'+ project + '</b>'

In javascript you don't have that luxury. I came up with a tiny function that does just that:

function ___(s)
{
var ss = arguments[0].split('___')
var out= [ss[0]]
for (var i=1;i<ss.length;i++){
out.push(arguments[i])
out.push(ss[i])
}
return out.join('')
}

So now you can do this:

s= ___('firstname:<b>___</b>, lastname:<b>___</b>, project:<b>___</b>',firstname, lastname, project)

I found that this syntax is actually better than its python cousin, because the symbol "___" implies that there's something to be filled, just like what we used to do when filling up a form with a pen.

It would look even more clear if we attach this "___()" function to the string prototype:

String.prototype._= function()
{

var ss = this.valueOf().split('___')
var out= [ss.shift()]
for (var i=0;i<ss.length;i++){
out.push(arguments[i])
out.push(ss[i])
}
return out.join('')
}

which allows this:

s= 'firstname:<b>___</b>, lastname:<b>___</b>, project:<b>___</b>'._(firstname, lastname, project)

Some other uses of this function:

Example-1:

alert('myArray=[___]'._(myArray))

Example-2:

tag = '<___ style="___">___</___>'
DIV = tag._('div','___','___','div')
SPAN = tag._('span','___','___','span')
adiv = DIV._("border:1px dashed blue; color:blue", "div example")
aspan= SPAN._("color:red;background-color:yellow", "span example")

The 2nd example gives:

div example
span example

Replace with a data in a hash


The above ___() function is already very helpful. But the original python % has one other feature:

data= {'fname':'Runsun'
,'lname':'Pan'
,'proj' :'wysiwyc'}
s= 'firstname:<b>%(fname)s</b>, lastname:<b>%(lname)s</b>, project:<b>%(proj)s</b>'%(data)

By inserting a (fname) into %s, you allow the data be in hash form. Now lets put this feature into the ___() function.

Stand-alone ___() function:

function ___(s)
{
var isObj=false
try {
isObj = arguments[1].constructor.toString().split('(')[0].split(' ')[1]=='Object'
}catch(e) {}

if (isObj)
{
var args = arguments[1]
for (var k in args) { s=s.split( '_'+k+'_').join( args[k] ) }
return s

}else{

var ss = arguments[0].split('___')
var out= [ss[0]]
for (var i=1;i<ss.length;i++){
out.push(arguments[i])
out.push(ss[i])
}
return out.join("")
}
}

Attached to string prototype:

String.prototype._= function()
{
var ss= this.valueOf()
var isObj=false
try {
isObj = arguments[0].constructor.toString().split('(')[0].split(' ')[1]=='Object'
}catch(e) {}

if (isObj)
{
var args = arguments[0]
for (var k in args) { ss=ss.split( '_'+k+'_').join( args[k] ) }
return ss

}else{

var ss = ss.split('___')
var out= [ss.shift()]
for (var i=0;i<ss.length;i++){
out.push(arguments[i])
out.push(ss[i])
}
return out.join("")
}
}

Now we can do this in javascript:

data= {fname:'Runsun'
,lname:'Pan'
,proj :'wysiwyc'}
s= 'firstname:<b>_fname_</b>, lastname:<b>_lname_</b>, project:<b>_proj_</b>'._(data)

Again, IMO, this looks better than its python cousin does.

UPDATE(7/23/07): See a regular expression version of the same function.