I have developed some sort of Jcrop initialization for a website, I managed to make my own namespace. Question I have is regarding this keyword. Every time I had to access my base object "aps" in any callback function I must wrap this in a variable (I have chosen word that). Is there any better way to do it? For example can I use call or apply methods? This is just a namespace so I could use simple aps.methodName but for sake of this example, please don't mind it. Here is my source code:
var aps;
$(function(){
aps = function(){
// private
// variables
var bgColor = '#f5f5f5';
var threshold = 370;
var threshold_width = 800;
return {
tmpl : $('#jcrop-template').html(),
upl_cont : {},
form : {},
logo_img : new Image(),
jcrop_api : null,
scaled_logo_url : '',
image_filename : '',
original_image_filename : '',
mime : '',
trueSize : '',
jcrop_init : function (oiFrameRes){
$('#logo_upload_form').find('img').hide();
this.scaled_logo_url = oiFrameRes.image_url;
this.logo_url = oiFrameRes.original_image_url;
this.original_image_filename = oiFrameRes.original_image_filename;
this.image_filename = oiFrameRes.image_filename;
this.mime = oiFrameRes.mime;
this.upl_cont = $('#facebox div#upload-container-d');
this.logo_img = new Image();
this.logo_img.that = this;
this.logo_img.name = 'logo';
this.logo_img.onload = function(){
this.true_width=this.width;
this.true_height=this.height;
this.that.resize_image();
this.that.resize_facebox();
this.that.display_image();
}
this.logo_img.src = this.logo_url;
},
resize_image : function(){
this.trueSize = '';
if(typeof (this.oSettings.trueSize)!=='undefined') delete(this.oSettings.trueSize);
if (this.logo_img.width > threshold){
if (this.logo_img.width > threshold_width){
this.trueSize = [ this.logo_img.width, this.logo_img.height ];
this.logo_img.height = this.logo_img.height / (this.logo_img.width / threshold_width);
this.logo_img.width = threshold_width;
}
}
},
resize_facebox : function(){
var width = (this.logo_img.width > threshold) ? this.logo_img.width : threshold ;
$('#facebox').css({
left : $(window).width() / 2 - width / 2
}).
find('div.change-size').css({'width': width+30});
},
display_image : function (){
if (this.jcrop_api === null) {
$logo_img = $(this.logo_img).css({'display':'block','margin-left':'auto','margin-right':'auto'})
if (this.upl_cont.find('#logo-container-d>img').length > 0){
if (this.upl_cont.find('#logo-container-d>img').attr('src').length > 0){
this.upl_cont.find('#logo-container-d').empty().append($logo_img);
}
}
else {
this.upl_cont.append(this.tmpl).find('#logo-container-d').append($logo_img);
}
var that = this;
if (typeof (this.upl_cont.find('#jcrop-menu1 a').data('events')) === 'undefined'){
this.upl_cont.find('#jcrop-menu1 a').click(function(){
if (this.href.indexOf('#crop')>-1){
$(this).closest('div').hide();
that.upl_cont.find('#jcrop-menu2').show();
that.setup_crop();
}
if (this.href.indexOf('#close')>-1){
manageIframeResponse();
}
location.hash = '';
return false;
});
}
}
else {
this.reset();
}
},
reset : function(){
$('#jcrop-menu2',this.upl_cont).find('a').unbind('click').end().hide();
$('#jcrop-coords-f',this.upl_cont).find('input[type="text"]').each(function(){this.value="";}).end().hide();
$('#jcrop-menu1',this.upl_cont).find('a').unbind('click').end().show();
this.jcrop_api.destroy();
this.jcrop_api=null;
this.display_image();
},
send_form : function (){
var sPost = $(this.form).find('input[name="image_filename"]').val(this.image_filename).end()
.find('input[name="original_image_filename"]').val(this.original_image_filename).end()
.find('input[name="mime"]').val(this.mime).end()
.find('input[name="user_url"]').val($('#logo_upload_base_url').val()).end()
.find('input[name="user_key"]').val($('#logo_upload_user_key').val()).end()
.serialize();
$.ajax({
url:'iframe_upload.php',
type:'POST',
data: sPost,
success : function(response){
manageIframeResponse();
},
dataType : 'json'
});
},
setup_crop : function (){
var that = this;
if (this.jcrop_api === null) {
this.form = this.upl_cont.find('form#jcrop-coords-f').get(0);
this.upl_cont.find('#jcrop-menu2>a').click(function(){ that.send_form();return false; });
this.updateForm = function (){
var c = arguments[0];
that.form.x1.value=c.x;
that.form.x2.value=c.x2;
that.form.y1.value=c.y;
that.form.y2.value=c.y2;
that.form.h.value=c.h;
that.form.w.value=c.w;
}
this.oSettings.onSelect = this.updateForm;
if (typeof (this.trueSize) !== 'string' && $.isArray(this.trueSize)){
$.extend(this.oSettings,{'trueSize':this.trueSize});
}
$('#facebox #logo-container-d>img').Jcrop( this.oSettings, function(){
that.jcrop_api = this;
var _x1 = (that.logo_img.true_width*0.1).toFixed();
var _y1 = (that.logo_img.true_height*0.1).toFixed();
var _x2 = (that.logo_img.true_width*0.9).toFixed();
var _y2 = (that.logo_img.true_height*0.9).toFixed();
that.jcrop_api.setSelect([0,0,that.logo_img.true_width,that.logo_img.true_height]);
that.jcrop_api.animateTo([_x1,_y1,_x2,_y2]);
});
}
},
updateForm : function (){},
oSettings : {
onSelect:'',
onChange:'',
keySupport: false,
bgColor:bgColor,
aspectRatio:1,
minSize:[0,0]
}
}
}();
$(document).bind('afterClose.facebox', function() {
if (aps.jcrop_api !=null) {
aps.jcrop_api.destroy();
aps.jcrop_api=null;
}
});
});
Anytime a function is invoked using function invocation*, the this
value is set to the global variable (or undefined
in strict mode)—even if you call the function from a method. Douglas Crockford has actually described this as a flaw in the language.
Saving the this
value into a variable that the function will have access to is the standard way of dealing with this.
If you really want to control what this
is in your callback, you could use apply
or call
. Both take as the first argument what you want this
to be set to. The difference is that apply
expects all the function's arguments to be passed as an array, while call
expects you to list them out individually.
So if, in your ajax callback, you wanted to call manageIframeResponse
, pass it the ajax call's response (I know your example didn't pass the response, I'm just illustrating how you would do it), and have its this
value be the same as the current object, you could do:
var self = this;
$.ajax({
success : function(response){
manageIframeResponse.apply(self, [response]); //<--- apply wants your arguments in array form
}
});
Or, since your parameters aren't already in array form, you could more simply use call
var self = this;
$.ajax({
success : function(response){
manageIframeResponse.call(self, response); //<---call takes the arguments listed out one at a time
}
});
* There are different ways to invoke a function.
Function invocation means you're just calling a function that happens to be in your current scope:
foo() //inside foo, this will be the global object (or undefined in strict mode)
Method invocation means that you're calling a function that's attached to an object
myObj.foo() //inside foo, this will be myObj
Here's an example of where this can trip you up if you're nor careful.
function objCreator() {
var y = "There";
function privateFunc() {
alert(y); //alerts There as expected
alert(this.someField); //undefined: whoops - this is the global object,
} //so there's no someField
return {
x: "Hi",
someField: "blah",
foo: function () {
alert(this.x);
privateFunc();
}
};
}
Consider this:
var aps = (function () {
// private variables
var private1;
var private2;
var private3;
var aps = {}; // the core object
aps.setup_crop = function () {
// use "aps" to access the core object
if ( !aps.jcrop_api ) { // etc.
};
// define other methods analogously
return aps;
})();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With