Classes, Options in Mootools/Prime - javascript

I will migrate a project from Mootools to Prime, but now I have a problem with the "setOptions", the function does not work as in Mootools where the options object will merge through supers up the proto chain and contain all the keys.
var A = prime({
options: {
name: 'image',
tag: 'span',
src: '/'
},
constructor: function(options){
this.setOptions(options);
},
getOptions: function(){
console.log(this.options);
}
});
var B = prime({
inherits: A,
options: {
name: 'B'
},
some: function(){
return;
}
});
mixin(A, Options);
mixin(B, Options);
var b = new B({
tag: 'div'
});
b.getOptions(); //the result should be: {name: "B", tag: "div", src: "/"} but is {name: "B", tag: "div"}
Thanks for any idea.

As answered on the mailing list - I even made you a repo - this works out of the box in primish.
https://bitbucket.org/DimitarChristoff/primish-example/src
slightly different syntax (sugar, it's been done to make mootools users happy) but it does also merge options like mootools did:
var A = prime({
options: {
name: 'image',
tag: 'span',
src: '/'
},
implement: [options],
constructor: function(options){
//console.debug(this.options);
this.setOptions(options);
},
getOptions: function(){
console.log(this.options);
}
});
var B = prime({
extend: A,
options: {
name: 'B'
}
});
var C = prime({
extend: B,
options: {
name: 'C'
}
});
var c = new C({
tag: 'div'
});
c.getOptions(); // as expected, all 3 options merged from right to left
Notice distinct lack of the extra mixin() nonsense and the familiar extend term rather than inherits
In prime you'd have to do something like:
var B = prime({
extend: A,
options: Object.merge(A.prototype.options, {
name: 'B'
})
});
where Object.merge is some util - either a prime util or lodash or whatever. in primish that would be prime.merge()

Related

jQuery on event handler losing scope of parent object

I have an object that has multiple functions being registered to events on page load. This seems to be working fine. However, I am having an issue getting the scope of the object once the event happens. this seems to only pull in the element that is being triggered by the event. I was wondering if anyone could help and tell me where I went wrong?
EDIT 1
I felt that code snippet was pretty concise to the problem. However, I can see where my question may be perceived as lazy.
I have an object that has multiple functions triggered off of events that are dynamically being wired up on load which can be seen here:
var collection = function() {
var _c = {};
_c.el = '#container',
_c.buildItems = function(){
var $el = $('#content2');
var template = Handlebars.compile($("#tpl-collection-item-row").html());
var data = [
{id: 1, val: 'test 1', text: 'this is a test... 1'},
{id: 2, val: 'test 2', text: 'this is a test... 2'},
{id: 3, val: 'test 3', text: 'this is a test... 3'},
];
$el.append(template(data));
},
_c.clickStuff = function() {
alert("hey!");
},
_c.moreClicking = function() {
alert("i knew this would work");
},
_c.events = [
{target: '.row', event: 'click', func: 'clickStuff'},
{target: '#gettingIt', event: 'click', func: 'moreClicking'}
],
_c.initialize = function() {
var _this = this;
$.each(this.events, function(){
var func = _this[this.func]
$(_this.el).on(this.event, this.target, func);
});
},
_c.render = function() {
this.initialize();
this.buildItems();
}
return _c;
}
_c.buildItems is a function that is called to trigger my handlebars template from _c.render(). Prior to this happening I am calling a function to wire up events listed in my _c.events array that take the target object and the type of event happening and mapping to the func name within my object. This is happening in my _c.initialize function.
The events seem to be working perfectly fine. However, I am unsure how to get the scope of my collection function from within functions like _c.clickStuff when they are triggered. I am unsure how to do this and it would be great if someone can explain to me where I went wrong?
Here is a fiddle of my code:
https://jsfiddle.net/0s8vb8m3/3/
You can just reference the _c in your code as it is in the scope of your "collection" function.
var collection = function() {
var _c = {
el: '#container'
};
_c.buildItems = function() {
var $el = $('#content2');
var template = Handlebars.compile($("#tpl-collection-item-row").html());
var data = [{
id: 1,
val: 'test 1',
text: 'this is a test... 1'
}, {
id: 2,
val: 'test 2',
text: 'this is a test... 2'
}, {
id: 3,
val: 'test 3',
text: 'this is a test... 3'
}];
$el.append(template(data));
};
_c.clickStuff = function() {
console.log(_c.el);
};
_c.moreClicking = function() {
alert("i knew this would work");
};
_c.events = [{
target: '.row',
event: 'click',
func: 'clickStuff'
}, {
target: '#gettingIt',
event: 'click',
func: 'moreClicking'
}];
_c.initialize = function() {
$.each(_c.events, function(index, event) {
var func = _c[event.func];
$(_c.el).on(event.event, event.target, func);
});
};
_c.render = function() {
_c.initialize();
_c.buildItems();
};
return _c;
}
var c = collection();
c.initialize();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container">
<div class="row">Blah1</div>
<div class="row">Blah2</div>
<div class="row">Blah3</div>
</div>

Getting id or name of folder in fuelux tree

I'm trying to get the id or name of the selected folder in a fuelux tree but couldnt manage to get it done.
My tree is a classic folder/file type tree and I want to be able to see the id of the folder when I click on a file.
this is my datasource for tree
var treeDataSource = new DataSourceTree({
data: [
{ name: 'Elektronik Belgelerim', type: 'folder', 'icon-class': 'blue', additionalParameters: { id: 'F1' } },
{ name: 'Gelen Kutusu', type: 'folder', 'icon-class': 'blue', additionalParameters: { id: 'F2' } },
{ name: 'Giden Kutusu', type: 'folder', 'icon-class': 'blue', additionalParameters: { id: 'F3' } },
{ name: 'Çöp Kutusu', type: 'folder','icon-class':'green', additionalParameters: { id: 'I1' } },
//{ name: 'Çöp Kutusu', type: 'item', 'icon-class': 'success', additionalParameters: { id: 'F4' } },
//{ name: 'Reports', type: 'item', additionalParameters: { id: 'I1' } },
//{ name: 'Finance', type: 'item', additionalParameters: { id: 'I2' } }
],
delay: 400
});
js function for tree begins like this inside tree-custom.js
var e = function (e, i) {
this.$element = t(e), this.options = t.extend({}, t.fn.tree.defaults, i), this.$element.on("click", ".tree-item", t.proxy(function (t) {
this.selectItem(t.currentTarget)
}, this)), this.$element.on("click", ".tree-folder-header", t.proxy(function (t) {
this.selectFolder(t.currentTarget)
}, this)), this.render()
};
and this is where I add the links under folders again inside trree-custom.js. Very primitive I know but that's all I can do with my current skillset. The part I added is between quotes. Rest came with beyondadmin theme and looks like usual fuelux.
selectFolder: function (e) {
//alert("testselectFolder");
//
//alert($('#myTree').tree({ dataSource: dataSource }));
var i, n, r, o = t(e),
s = o.parent(),
a = s.find(".tree-folder-content"),
l = a.eq(0);
//-----------------------------------------------
var li = $('<li>');
var TcgbLink = $('<a href=/E-Belge/Main/Folder/Inbox/?Type=1&DocumentTypeId=3>e-TCGB</div>' +"</br>");
var FaturaLink = $('<a href=/E-Belge/Main/Folder/Inbox/?Type=1&DocumentTypeId=4>e-Fatura</div>' + "</br>");
var Dolasim = $('<a href=>e-Dolasim Belgesi</div>');
li.append(FaturaLink);
a.append(li);
li.append(TcgbLink);
a.append(li);
li.append(Dolasim);
a.append(li);
//-----------------------------------------------
o.find(".fa.fa-folder").length ? (i = "opened", n = ".fa.fa-folder", r = "fa fa-folder-open", l.show(), a.children().length || this.populate(o)) : (i = "closed", n = ".fa.fa-folder-open", r = "fa fa-folder", l.hide(), this.options.cacheItems || l.empty()), s.find(n).eq(0).removeClass("fa fa-folder fa-folder-open").addClass(r), this.$element.trigger(i, o.data())
},
Now these links are being generated under all 4 folders. I want to be able to get the id (or name, preferably Id) of the folder so I can assign new Type parameters to querystring.
So far I tried to reach the id with this.data.id to no avail.
Instead of injecting the folder's children in the selectFolder callback, it is recommended to add the children via the dataSource callback (as in this example code: http://getfuelux.com/javascript.html#tree-usage-javascript).
The first argument to the dataSource is the "parent data" when you click on a tree node (with the second argument being the callback that you send the new array of child data).
This way you can use the selected event for getting your ID, because it gets the jQuery data passed to it.

jQuery TextExt: Tags with custom data objects

I'm currently struggling with implementing a jQuery plugin to my site for tags (with custom data objects) with autocomplete. jQuery TextExt can be found here (http://textextjs.com/). I'm currently struggling with using custom data objects for each tag, which can only be chosen from what autocompletes. Based on this example (http://textextjs.com/manual/examples/tags-with-custom-data-objects.html) I'm trying to figure out how to return both "name" and "id" when a tag is chosen. Does anyone know how to achieve this or point me in the correct direction?
Perhaps the answer is somewhere in this example (http://textextjs.com/manual/examples/filter-with-suggestions.html)?
Here's what I have written, which isn't working (it only returns the name, I've tried adding 'item.id' to the functions but that didn't work for me either):
<script type="text/javascript">
jQuery(document).ready(function( $ ){
jQuery('#textarea').textext({
plugins: 'tags',
items: [
{ name: 'PHP', id: '1' },
{ name: 'Closure', id: '2' },
{ name: 'Java', id: '3' }
],
ext: {
itemManager: {
stringToItem: function(str)
{
return { name: str };
},
itemToString: function(item)
{
return item.name ;
},
compareItems: function(item1, item2)
{
return item1.name == item2.name;
}
}
}
});
})
</script>
Your itemManager code should probably look like this, you will need to store the suggestions in a custom array to look up their relevant ids in the stringToItem Method
itemManager: {
items: [], // A custom array that will be used to lookup id
stringToItem: function(str)
{
//Lookup id for the given str from our custom array
for (var i=0;i<this.items.length;i++)
if (this.items[i].name == str) {
id = this.items[i].id;
break;
}
return { name: str, id: id };
},
itemToString: function(item)
{
//Push items to our custom object
this.items.push(item);
return item.name ;
},
compareItems: function(item1, item2)
{
return item1.name == item2.name;
}
}

Custom UI dropdown button not showing up all commands

I am doing like below to group set of commands into single dropdown but all items are not showing up
CKEDITOR.config.toolbar = [{
name: 'paragraph',
groups:['list', 'indent', 'blocks', 'align', 'bidi'],
items: ['More']
}];
CKEDITOR.replace(editorName, {
on: {
pluginsLoaded: function () {
var editor = this,
items = {}, groupName = 'Justify_Group',
config = CKEDITOR.config;
var more_Group = 'More_Group';
editor.addMenuGroup(more_Group);
var moreButtons = {
Subscript: 'Sub Script',
Superscript: 'Super Script',
NumberedList: 'Numbered List',
BulletedList: 'Bullet List',
Outdent: 'Outdent',
Indent: 'Indent',
Blockquote: 'Blockquote',
RemoveFormat: 'Remove Format'
}, moreItems = {};
for (var i in moreButtons) {
var v = moreButtons[i];
moreItems[i.toLowerCase()] = {
label: v,
group: more_Group,
command: i.toLowerCase(),
order: 1
};
}
editor.ui.add('More', CKEDITOR.UI_MENUBUTTON, {
label: 'More',
name: 'More',
modes: {
wysiwyg: 1
},
onMenu: function () {
var active = {};
for (var p in moreItems)
active[p] = null
return active;
}
});
}
}
});
But same thing works well for Justify buttons. In above list of commands only Bulleted Lists are showing up in dropdown that too with (properties) text added to given label.
How can I fix this issue
Well the only thing that you're missing is adding proper menu items.
It's done with editor.addMenuItem.
Why adding menu items?
CKEditor UI menus work with menu items, not buttons or anything like that.
Mentioned Bullet List / Numbered List were just a coincidence, most likely because other plugin registered them to use it in context menu (as it reuses menu objects).
How to fix it in your case?
To make things easier we'll use editor.addMenuItems.
And it would be it, if you wouldn't include lowercasing for command names. I'll fix it as well.
CKEDITOR.config.toolbar = [{
name: 'paragraph',
groups:['list', 'indent', 'blocks', 'align', 'bidi'],
items: ['More']
}];
CKEDITOR.replace(editorName, {
on: {
pluginsLoaded: function () {
var editor = this,
items = {}, groupName = 'Justify_Group',
config = CKEDITOR.config;
var more_Group = 'More_Group';
editor.addMenuGroup(more_Group);
var moreButtons = {
subscript: 'Sub Script',
superscript: 'Super Script',
numberedlist: 'Numbered List',
bulletedlist: 'Bullet List',
outdent: 'Outdent',
indent: 'Indent',
blockquote: 'Blockquote',
removeFormat: 'Remove Format'
}, moreItems = {};
for (var i in moreButtons) {
var v = moreButtons[i];
moreItems[i.toLowerCase()] = {
label: v,
group: more_Group,
command: i,
order: 1
};
}
editor.addMenuItems( moreItems );
editor.ui.add('More', CKEDITOR.UI_MENUBUTTON, {
label: 'More',
name: 'More',
modes: {
wysiwyg: 1
},
onMenu: function () {
var active = {};
for (var p in moreItems)
active[p] = null
return active;
}
});
}
}
});
What can go weird?
You'll need to note that the default menu implementation won't show menu items of which related command status is equal to CKEDITOR.TRISTATE_OFF.
You can always check this condition by evaluating editor.commands.bold.state !== CKEDITOR.TRISTATE_DISABLED.
You can workaround this problem by passing onClick callback that will call your command, rather than providing command as a string, but then you'll need also to manually care about command state. Similar trick was used in language plugin. Further explainations goes out of scope of this question.

How to represent form options that depend on other forms

I have a form that is suposed to help to user to choose a specific thing at the end, but as the user fills the first options, the others below change. Something like this:
Type:
{
t1:{
Number of X:{
1:{...}
2:{...}
}
Number of Y:{...}
}
t2:{
Number of X:{
100:{...}
200:{...}
}
Number of Y:{...}
}
}
The user has the field Type with the options t1 and t2, when they choose t1, the field "Number of X" will be filled with 1 and 2, if they choose t2, the field "Number of X" will be filled with 100 and 200, and so on. Some of the choices depend on more than one field, its not straight down dependency (something like, if the user chooses "Number of X" = 100 then Foo is "A", else, Foo can be "A", "B" or "C", but Foo is not bellow "Number of X").
I tried a really naive implementation where I would set up event listeners on every field and see their changes, but eventually the code started growing out of control and I have a bunch of $("#foo").change(function(){...}); and its not imediatly obvious that the field listening to this is bar and not fbar.
I also tried JSON (as the example above), but there's a lot of repetition, the deeper the tree grows and the number of possibilites increase, I have to write the same fields again and again. Sometimes choosing t1 will change an option directly even though its not directly bellow it, and even though it usually depends on another field entirely, and that's more repetition in JSON.
How do I approach this problem? Is there a readable solution? Too much code is not the problem, as long as one can look at the code and understand the dependencies and their effects.
A code example (kinda like my code right now):
HTML:
<select id="type">
<option value=1>a</option>
<option value=2>b</option>
</select>
<select id="numOfX">
</select>
<select id="numOfY">
</select>
js:
$("#type").change(function()
{
if($("#type").val() == 1)
{
$("#numOfX").append(new Option(1, "1", false, false));
$("#numOfX").append(new Option(2, "2", false, false));
}
else if($("#type").val() == 2)
{
$("#numOfX").append(new Option(1, "100", false, false));
$("#numOfX").append(new Option(2, "200", false, false));
}
});
$("#numOfX").change(function()
{
...
});
Update - Add example
Have you try backbone.js library? It will make the Javascript code more manageable by adding models & structures. There is a learning curve though but it is really great. Once you learn Backbone, you can make use of the Backbone Forms plugin which will help in the dropdown management. Below is the demo link & sample code:
Example 1
$(function() {
var cities = {
'UK': ['London', 'Manchester', 'Brighton', 'Bristol'],
'USA': ['London', 'Los Angeles', 'Austin', 'New York']
};
var subAreas = {
'London' : ['L1', 'L2', 'L3', 'L4'],
'Manchester' : ['M1', 'M2', 'M3', 'M4'],
'Brighton' : ['B1', 'B2', 'B3', 'B4'],
'Bristol' : ['BR1', 'BR2', 'BR3', 'BR4'],
'Los Angeles' : ['LA1', 'LA2', 'LA3', 'LA4'],
'Austin' : ['A1', 'A2', 'A3', 'A4'],
'New York' : ['NY1', 'NY2', 'NY3', 'NY4']
};
//The form
var form = new Backbone.Form({
schema: {
country: { type: 'Select', options: ['UK', 'USA'] },
city: { type: 'Select', options: cities.UK },
subArea: { type: 'Select', options: subAreas[cities.UK[0] ] }
}
}).render();
form.on('country:change', function(form, countryEditor) {
var country = countryEditor.getValue(),
newOptions = cities[country];
form.fields.city.editor.setOptions(newOptions);
var city = newOptions[0],
areaOptions = subAreas[city];
form.fields.subArea.editor.setOptions(areaOptions);
});
form.on('city:change', function(form, cityEditor) {
var city = cityEditor.getValue(),
newOptions = subAreas[city];
form.fields.subArea.editor.setOptions(newOptions);
});
//Add it to the page
$('body').append(form.el);
});​
Example 2
$(function() {
var cities = {
'UK': ['London', 'Manchester', 'Brighton', 'Bristol'],
'USA': ['London', 'Los Angeles', 'Austin', 'New York']
};
var subAreas = {
'UK.London' : ['L1', 'L2'],
'USA.London' : ['L3', 'L4'],
'UK.Manchester' : ['M1', 'M2', 'M3', 'M4'],
'UK.Brighton' : ['B1', 'B2', 'B3', 'B4'],
'UK.Bristol' : ['BR1', 'BR2', 'BR3', 'BR4'],
'USA.Los Angeles' : ['LA1', 'LA2', 'LA3', 'LA4'],
'USA.Austin' : ['A1', 'A2', 'A3', 'A4'],
'USA.New York' : ['NY1', 'NY2', 'NY3', 'NY4']
};
var hashFunc = function(country, city){
return country + "." + city;
};
//The form
var form = new Backbone.Form({
schema: {
country: { type: 'Select', options: ['UK', 'USA'] },
city: { type: 'Select', options: cities.UK },
subArea: { type: 'Select', options: subAreas[ 'UK.London' ] }
}
}).render();
form.on('country:change', function(form, countryEditor) {
var country = countryEditor.getValue(),
newOptions = cities[country];
form.fields.city.editor.setOptions(newOptions);
var city = newOptions[0],
areaOptions = subAreas[hashFunc(country, city) ];
form.fields.subArea.editor.setOptions(areaOptions);
});
form.on('city:change', function(form, cityEditor) {
var city = cityEditor.getValue(),
newOptions = subAreas[hashFunc(form.getValue().country, city)];
form.fields.subArea.editor.setOptions(newOptions);
});
//Add it to the page
$('body').append(form.el);
});​
As you also develop for mobile (probably Phonegap), you can also try ZeptoJS as an alternative for jQuery. It will improve the speed alot.
The task outlined is complex because of dependencies, so you must think of the ways to define your dependencies. Here is one way I would do it:
Define models which handle data.
Define dependencies.
Manage dependencies.
Below you can see a conceptual model how I see this all implemented (at the end of my answer I describe things which are not provided in this pseudo code):
//data/model structure for Type.
var type = {
//list all values.
values: [
{ id: 1, text: 't1', visible: true },
{ Id: 2, text: 't2', visible: true }
],
//evaluates visibility of item using dependencies.
//depends on nothing, so takes no arguments except item.
evaluate: function(item) {
return; //depends on nothing.
},
// this event fires when selected item changes.
onChange: event
};
//data/model structure for number of X.
var numberOfX = {
//list all values.
values: [
{ id: 1, text: '1', visible: true },
{ id: 2, text: '2', visible: true },
{ id: 3, text: '100', visible: true },
{ id: 4, text: '200', visible: true }
],
//evaluates visibility of item using dependencies.
//since numberOfX depends on Type, it takes type as second argument.
//it would take more arguments if it depended on other things too.
evaluate: function(item, type) {
// next line will fire onChange event.
item.visible =
( [1,2].indexOf(item.id) >=0 && type.id == 1 ) ||
( [3,4].indexOf(item.id) >=0 && type.id == 2 );
},
// this event fires when selected item changes.
onChange: event
};
//data/model structure for number of Y.
var numberOfY = { /* omitted. This is similar to the previous ones */ }
//numberOfX depends on Type.
//if it depended on more objects, then we would pass them as additional arguments.
register_dependency(numberOfX, type);
//numberOfY depends on Type.
register_dependency(numberOfY, type);
//... etc: define other dependencies.
Event mechanism is not there in JavaScript, but implementing one is not hard. You can use some framework for that as well.
register_dependency function builds a graph of dependencies simply by registering for events, as described below (managing dependencies):
When onChange event fires on any model, evaluate is called for each item in the dependency tree. For example, when type.onChange fires, we have numberOfX and numberOfY objects. Their values array is enumerated in loop and evaluate is called for each item (passing item and type as arguments).
Conclusion: although this code seems complex, still it's more self-descriptive and allows to have graph of dependencies between multiple objects on the page. Also, all the complexity lays on the toolkit/framework level, which could be easily re-used when implemented only once.
EDIT: Forgot to outline that you would need to have some mechanism to bind to this kind of model and show it on the page, which is also trivial. For example, have a look at knockout.js.

Categories