It''s been a few days since I posted my last two emails (of which, no one replied to) so I''ve gone ahead and coded a working slider class as well as a ''resizeable'' class that allows a user to drag and resize the element. I also linked these two together as I want to have slider behavior that can resize the resizeable element. The end result is both the sliders (one horizontal and one vertical) and the resizeable element work together. The one downside is that my slider object is linked to the other. So I would like to break it out and make either a more complex subclass or add behavior that I can override if the complexity is necessary. I have two questions, which I would greatly appreciate at least someone responding to (you can do so privately if this is inappropriate for this list). First, can you override a method that is defined in an existing class by passing it into the options properties? If so what are the key steps? Second, is there any documentation that goes over the extending of javascript classes in the prototype or scriptaculous libraries? Once I break my slider class into either a simpler overrideable class or into a base prototype with a more complex subclass then I''d consider contributing it to scriptaculous. If this sort of code isn''t welcome, then I won''t bother. Cheers, Marty
On 8/18/05, Marty Haught <marty@razorstream.com> wrote:> First, can you override a method that is defined in an existing class by > passing it into the options properties? If so what are the key steps?You can, but you have to edit the existing class to check to see if the new method is passed in, and if so, override it. For instance, in the Ajax.Updater class you can see the following statement which uses the function passed in via the options parameter, or uses an empty function: var onComplete = this.options.onComplete || Prototype.emptyFunction; Second, is there any documentation that goes over the extending of> javascript classes in the prototype or scriptaculous libraries?Although it isn''t directly related to the scriptaculous library, the following page has some good links with information on javascript classes in general that you might find useful: http://wiki.script.aculo.us/scriptaculous/show/JavaScriptDocumentation Once I break my slider class into either a simpler overrideable class or> into a base prototype with a more complex subclass then I''d consider > contributing it to scriptaculous. If this sort of code isn''t welcome, > then I won''t bother.I am very interested in a slider class. Please do contribute it! -------------- next part -------------- An HTML attachment was scrubbed... URL: http://wrath.rubyonrails.org/pipermail/rails-spinoffs/attachments/20050818/8850f769/attachment.html
On Thursday 18 August 2005 23:59, Marty Haught wrote:> It''s been a few days since I posted my last two emails (of which, no > one replied to) so I''ve gone ahead and coded a working slider class > as well as a ''resizeable'' class that allows a user to drag and resize > the element. I also linked these two together as I want to have > slider behavior that can resize the resizeable element. The end > result is both the sliders (one horizontal and one vertical) and the > resizeable element work together. The one downside is that my slider > object is linked to the other. So I would like to break it out and > make either a more complex subclass or add behavior that I can > override if the complexity is necessary.This sounds like a perfect case for a model-observer relationship. What you have is a resizable element and different two different kinds of objects that can change its size and need to react to size changes. The first part is easy, just add functions (well, methods) to your resizable element that allow to set the size from the outside. For the second part, the resizable object should offer a means to registered so-called observers (aka listeners) that want to be informed of relevant changes.> First, can you override a method that is defined in an existing class > by passing it into the options properties? If so what are the key > steps?In JavaScript all properties are created equal, no matter if they hold a string, number, function, or some other object. Also, objects have a prototype -- the namesake of prototype.js. Simply put, if JavaScript looks for a property of an object and can''t find it in the object itself, it further looks in the special prototype property for that property. If you want inheritance, you can get it by convention. With the help of prototype.js it would look like this var BaseClass = Class.create(); BaseClass.prototype = { aBaseMethod: function() { ... } } var DerivedClass = Class.create(); DerivedClass.prototype = Object.extend(new BaseClass(), { aNewMethod: function() { alert(''Hi!'') } }) Spelled out, what happens here is this (ignoring the initialize method): var BaseClass = function() {}; BaseClass.prototype = { aBaseMethod: function() { alert(''aBaseMethod'') } } var DerivedClass = function() {}; var parent = new BaseClass(); DerivedClass.prototype.aBaseMethod = parent.aBaseMethod; DerivedClass.prototype.aNewMethod = function() { alert(''aNewMethod'') };> Second, is there any documentation that goes over the extending of > javascript classes in the prototype or scriptaculous libraries?Not directly related, but http://www.sitepoint.com/article/javascript-objects The Object chapter in David Flanagan: JavaScript. The Definitive Guide. O''Reilly 2002> Once I break my slider class into either a simpler overrideable class > or into a base prototype with a more complex subclass then I''d > consider contributing it to scriptaculous. If this sort of code > isn''t welcome, then I won''t bother.Commenting would have been easier if you had included your code or posted it somewhere. Michael -- Michael Schuerig Those people who smile a lot mailto:michael@schuerig.de Watch the eyes http://www.schuerig.de/michael/ --Ani DiFranco, Outta Me, Onto You
>This sounds like a perfect case for a model-observer relationship. What>you have is a resizable element and different two different kinds of >objects that can change its size and need to react to size changes. > >The first part is easy, just add functions (well, methods) to your >resizable element that allow to set the size from the outside. For the >second part, the resizable object should offer a means to registered >so-called observers (aka listeners) that want to be informed of >relevant changes. I definitely agree and this was the way I was headed. I''m been trying a few things this morning. First, I realized that js doesn''t seem to support arrays of objects. When I tried it it turned my objects into strings. So I switched gears and tried just registering a simple object. The first part of the code is run in the initialize method. This takes a ''listener'' class with one method that gets called when the slider moves. After that is the example of the slider creation in the html page, including the listener class. // snip from initialize method where the listener is registered this.listener = false; if(options.listener){ this.listener = options.listener; } // Initial call to listener during initialization if(this.listener && this.listener.updatingValue){ this.listener.updatingValue(this); } // Example of the slider creation in the html page var s1 = new Slider(''slider_1'',''track_1'', {axis:''horizontal'', sliderValue:80, alignY: -5, alignX: -5, listener: {updatingValue: function(parent){ var videoFrame = $(''vidFrame''); if(parent.isVertical()){ document.forms[0].yValue.value = parent.sliderValue +", style.top="+parent.handle.style.top + ", trackOffset="+parent.track.offsetTop; videoFrame.style.height = parent.sliderValue +"px"; } else { document.forms[0].xValue.value = parent.sliderValue +", style.left="+parent.handle.style.left; videoFrame.style.width = parent.sliderValue +"px"; } } } }); Was this more or less what you were thinking? Or did you have something more complex in mind? >In JavaScript all properties are created equal, no matter if they hold a >string, number, function, or some other object. Also, objects have a >prototype -- the namesake of prototype.js. Simply put, if JavaScript >looks for a property of an object and can''t find it in the object >itself, it further looks in the special prototype property for that >property. > >If you want inheritance, you can get it by convention. With the help of >prototype.js it would look like this // snip // >Not directly related, but >http://www.sitepoint.com/article/javascript-objects > >The Object chapter in >David Flanagan: JavaScript. The Definitive Guide. O''Reilly 2002 Thank you. It''s starting to make more sense. >Commenting would have been easier if you had included your code or >posted it somewhere. I have some more logic to put into the sliders now that I have the ''listener'' stuff working. After that''s finished I''ll submit it to the list for feedback. I''m not sure if my approach is elegant enough but so far it''s working. Haven''t tested it out in all browsers yet... Cheers, Marty
On Friday 19 August 2005 21:33, Marty Haught wrote:> >This sounds like a perfect case for a model-observer relationship. > > What you have is a resizable element and different two different > > kinds of objects that can change its size and need to react to > > size changes. > > > >The first part is easy, just add functions (well, methods) to your > >resizable element that allow to set the size from the outside. For > > the second part, the resizable object should offer a means to > > registered so-called observers (aka listeners) that want to be > > informed of relevant changes. > > I definitely agree and this was the way I was headed. I''m been > trying a few things this morning. First, I realized that js doesn''t > seem to support arrays of objects. When I tried it it turned my > objects into strings.It definitely works to have arrays of objects. Have a look at prototype.js and the script.aculo.us code. Include some code and we can try to figure out what went wrong in your case.> So I switched gears and tried just registering > a simple object. The first part of the code is run in the initialize > method. This takes a ''listener'' class with one method that gets > called when the slider moves.I''d recommend not to use a listener object. That would be the idiomatic thing in Java, say, but not in JavaScript. In JavaScript, a closure is much more natural. With the help of prototype.js I''d do it like this (untested) var Subject = Class.create(); Subject.prototype = { initialize: function(){}, // [1] observers: [], addObserver: function(observer) { this.observers.push(observer); }, notify: function() { for (var i = 0; i < this.observers.length; i++) { this.observers[i](this); } } } var MyObserver = Class.create(); MyObserver.prototype = { initialize: function(){}, // [1] _callback: function(subject) { ... }, callback: function() { return this._callback.bind(this); // [2] } } var subject = new Subject(); var observer = new Observer(); subject.addObserver(observer.callback()); [1] prototype.js needs this in concrete classes [2] binds the function _callback to the object currently denoted by this> // Example of the slider creation in the html page > var s1 = new Slider(''slider_1'',''track_1'', {axis:''horizontal'', > sliderValue:80, alignY: -5, alignX: -5, > listener: > {updatingValue: function(parent){ > var videoFrame = $(''vidFrame''); > if(parent.isVertical()){ > document.forms[0].yValue.value = parent.sliderValue > +", style.top="+parent.handle.style.top + ", > trackOffset="+parent.track.offsetTop; > videoFrame.style.height = parent.sliderValue +"px"; > > } else { > document.forms[0].xValue.value = parent.sliderValue > +", style.left="+parent.handle.style.left; > videoFrame.style.width = parent.sliderValue +"px"; > } > } > } > }); > > > Was this more or less what you were thinking? Or did you have > something more complex in mind?More or less. I wouldn''t put code like that into a page, however, if I could find a way to avoid it. It could be placed in an external javascript file and there''s probably room for more abstraction. Michael -- Michael Schuerig Those who call the shots mailto:michael@schuerig.de Are never in the line of fire http://www.schuerig.de/michael/ --Ani DiFranco, Not So Soft
>It definitely works to have arrays of objects. Have a look at>prototype.js and the script.aculo.us code. Include some code and we can >try to figure out what went wrong in your case. Okay, here''s the code that I last tried. I definitely get in the array that I passed in but instead of each element in the array being an object it''s a string and the method that I attached to each object was undefined. // initalize() code: this.listeners = new Array(); this.hasListeners = false; if(options.listeners){ var listeners = options.listeners; if((typeof listeners == ''object'') && (listeners.constructor == Array)) { for(var i=0; i<listeners.length; i++) this.listeners.push(Object.extend({}, listeners[i] || {})); this.hasListeners = true; } else { this.listeners.push(Object.extend({}, listeners || {})); this.hasListeners = true; } } if(this.hasListeners){ alert("calling listeners count:"+ this.listeners.length); for(listener in this.listeners){ alert("listener="+ typeof listener + ", updatingValue="+ typeof listener.updatingValue + "\n"+ listener); if(listener.updatingValue){ alert("calling updatingValue"); listener.updatingValue(this); } } } // Page code: var s2 = new Slider(''slider_2'',''track_2'', {axis:''vertical'', sliderValue:80, alignX: -5, alignY: -5, listeners: [ {updatingValue: function(parent){ var videoFrame = $(''vidFrame''); if(parent.isVertical()){ document.forms[0].yValue.value = parent.sliderValue +", style.top="+parent.handle.style.top + ", trackOffset="+parent.track.offsetTop; videoFrame.style.height = parent.sliderValue +"px"; } else { document.forms[0].xValue.value = parent.sliderValue +", style.left="+parent.handle.style.left; videoFrame.style.width = parent.sliderValue +"px"; } } }, {updatingValue: function(parent){ alert("Second listener here!"); } } ] }); >I''d recommend not to use a listener object. That would be the idiomatic >thing in Java, say, but not in JavaScript. In JavaScript, a closure is >much more natural. > >With the help of prototype.js I''d do it like this (untested) > >var Subject = Class.create(); >Subject.prototype = { > initialize: function(){}, // [1] > observers: [], > addObserver: function(observer) { > this.observers.push(observer); > }, > notify: function() { > for (var i = 0; i < this.observers.length; i++) { > this.observers[i](this); > } > } >} > >var MyObserver = Class.create(); >MyObserver.prototype = { > initialize: function(){}, // [1] > _callback: function(subject) { > ... > }, > callback: function() { > return this._callback.bind(this); // [2] > } >} > >var subject = new Subject(); >var observer = new Observer(); >subject.addObserver(observer.callback()); > >[1] prototype.js needs this in concrete classes >[2] binds the function _callback to the object currently denoted by this Interesting. I did see this approach used on the Droppables code. So the _callback function is where you put your custom code per slider? Is the Subject the slider then? So I would be adding an array of observers per slider and thus the slider''s update code would call the notify method (which in term calls each observer). I''m not sure I understand the addObserver code here: subject.addObserver(observer.callback()); So you''re registering the method and not the observer itself? Or is the observer class just a placeholder and you just pass in the method you want to execute on notify? >More or less. I wouldn''t put code like that into a page, however, if I >could find a way to avoid it. It could be placed in an external >javascript file and there''s probably room for more abstraction. I suppose I could easily migrate that code to a js fie. However, I envision that each slider would (if they chose) launch custom code when updated. So slider1 might update inputFieldA and resize ElementX, while slider2 might update inputFieldB and resize ElementY. I see that as being page specific and probably wouldn''t see much reuse, though I suppose you might be able to abstract out a few things. Since I''m new the javascript approach I don''t have enough experience to see where I could refactor for DRYer code quite yet. Cheers, Marty
On 22/08/05, Marty Haught <marty@razorstream.com> wrote:> Okay, here''s the code that I last tried.An URL with a live demo would be better.> this.listeners.push(Object.extend({}, listeners[i] || {}));this.listeners.push(listeners[i]); , no need for Object.extend()> for(listener in this.listeners){this.listeners is a Array, so you should better loop over its numeric indices. In IE5, you would also get the push and pop method in the loop, because they are added by prototype,js as they are missing in IE5''s JScript.> Interesting. I did see this approach used on the Droppables code.> So > the _callback function is where you put your custom code per slider? Is > the Subject the slider then? So I would be adding an array of observers > per slider and thus the slider''s update code would call the notify > method (which in term calls each observer).Yes. Although for your slider, the MyObserver object might not be necessary, if you just want to execute a function. subject.addObserver(thefunction); would be it in this case.> I''m not sure I understand the addObserver code here: > > subject.addObserver(observer.callback()); > > So you''re registering the method and not the observer itself?Yes.> Or is the > observer class just a placeholder > and you just pass in the method you > want to execute on notify?In the example, the MyObserver class is not that useful, as it does not have any properties to be used in the callback.> I suppose I could easily migrate that code to a js fie. However, I > envision that each slider would (if they chose) launch custom code when > updated.Via the observer mechanism.> So slider1 might update inputFieldA and resize ElementX, while > slider2 might update inputFieldB and resize ElementY.Here comes the MyObserver class.> I see that as > being page specific and probably wouldn''t see much reuseI think there are general cases like updating a form file or updating the content of a html element. In line with the drag/drop and effects, I these updates could be native to the slider with the fields to update passed in the options. Any other action, like your resizing, would be done through onUpdate specified callbacks (=observers).
[Callbacks with closures] On Monday 22 August 2005 18:01, Marty Haught wrote:> Interesting. ?I did see this approach used on the Droppables code. > ?So the _callback function is where you put your custom code per > slider? ?Is the Subject the slider then? ?So I would be adding an > array of observers per slider and thus the slider''s update code would > call the notify method (which in term calls each observer). > > I''m not sure I understand the addObserver code here: > > subject.addObserver(observer.callback()); > > So you''re registering the method and not the observer itself?Exactly. But there''s a trick. I''m registering a function that still has a binding refering to the observer object. See the following docs http://www.brockman.se/writing/method-references.html http://jibbering.com/faq/faq_notes/closures.html> ?>More or less. I wouldn''t put code like that into a page, however, > if I >could find a way to avoid it. It could be placed in an external > >javascript file and there''s probably room for more abstraction. > > I suppose I could easily migrate that code to a js fie. ?However, I > envision that each slider would (if they chose) launch custom code > when updated. ?So slider1 might update inputFieldA and resize > ElementX, while slider2 might update inputFieldB and resize ElementY.Then only put the code that does the "wiring" in the page and keep those parts that stay the same for all applications of the slider in an external file. Michael -- Michael Schuerig They tell you that the darkness mailto:michael@schuerig.de Is a blessing in disguise http://www.schuerig.de/michael/ --Janis Ian, From Me To You
Thanks guys. I believe I understand the approach. Sorry about not posting the code. Indeed that would be more useful. Currently my prototype only exists on an external address and will be a bit while before I put it on an external facing server. Though I could have popped it up on my personal website for demo purposes. The next question I have is where should I put my slider code? Should I append it to the end of an extending js file or make a new slider.js file? I have a bit more testing to do before I call it done but I''m close. Cheers, Marty