Halaman

Tampilkan postingan dengan label javascript. Tampilkan semua postingan
Tampilkan postingan dengan label javascript. Tampilkan semua postingan

Kamis, 09 Agustus 2012

Web page Al Bhed translator javascript (bookmarklet)

well long time no post :D

recently I don't have much to do and rather than dying from boredom I created a simple javascript to translate a web page to Al Bhed language (from Final Fantasy X if you're wondering)

The Base translation mapping come from http://nysa.cx/albhed/ and I just converted it to javascript.

Anyway here are the script:
javascript: (function(){var f=document.createElement('script'); f.src = 'http://al-bhed-page-translator-js.googlecode.com/files/albhed.js'; document.head.appendChild(f);})();
just copy and paste it into the browser address bar in the page you want to translate. (keep in mind that newer firefox block this kind of script, and you need to use bookmarklet for that)

To translate the page back to original, simply use this script:
javascript: (function(){alTranslate(true);})();

That's it :)

Known bugs:
- html encoded character such as   didn't translate properly, it's fixable but I'm too lazy to fix it :P
- huge page might causing the browser to freeze, also should be fixable by adding some timeout but again, I'm lazy :D

Senin, 04 Juli 2011

fixing option tag display none bug in webkit

The Problem

option tag using style display:none isn't hidden in browser using webkit engine (chrome, safari)
https://bugs.webkit.org/show_bug.cgi?id=8351

The workaround

using jQuery js framework with one simple function taken from jquery.extended_helper_2.6:
(function($) {
  $.fn.extend({
    /* hide select's specified option by removing the option and storing it in variable
     * this function is a workaround to fix webkit bug when hiding select's option:
     * https://bugs.webkit.org/show_bug.cgi?id=8351
     * hideSelectOptions(selector, options)
     * selector:
     * - string selector of options to hide
     * - null or undefined will not hide any options
     * - empty string will hide all options
     * - function will be ran for all options, return true to hide the options
     * options:
     * - toggleHide: true/false, activate/deactive toggle hide/show select if all options is hidden
     * - placeholder: string, add placeholder option if all options is hidden
     * usage:
     *  - $('select.certain_class').hideSelectOptions('.another_class'); // -> show previously hidden options and hide all options with 'another_class' class
     *  - $('select.certain_class').hideSelectOptions(''); // -> hide all options
     *  - $('select.certain_class').hideSelectOptions(); // -> show previously hidden options without hiding any options
     */
    hideSelectOptions: function(selector, options){
      if(!options) options = {};
      this.filter('select').each(function(){
        var $this = $(this);
        // restore previously hidden options and remove any placeholder option
        $this.append($this.data('hiddenOptions')).find('.placeholder_option').remove();

        // return if no selector specified
        if(!selector) return;
        
        // find, detach and store specified hidden options
        var hidden_options = $this.find('option');
        if($.isFunction(selector))
          hidden_options = hidden_options.deleteIf(function(){return !selector.call(this);});
        else
          hidden_options = hidden_options.filter(selector);
        $this.data('hiddenOptions',
          hidden_options
            .removeAttr('selected')
            .detach());

        // find visible options
        var visible_options = $this.find('option');

        // toggle hide/show select depend on visible option found
        if(options.toggleHide)
          $this.toggle(visible_options.length);
        if(options.placeholder && !visible_options.length)
          $this.append('');

        // select first option if no other visible option selected and trigger select change
        if(!visible_options.filter(':selected').length){
          visible_options.filter(':first').attr('selected', 'selected');
          $this.trigger('change');
        }
      });
      return this;
    }
  });
})(jQuery);

The Demonstration

Selasa, 03 Mei 2011

Aborting AJAX Request

For some functions like autocomplete, usually we send ajax request whenever user stop typing (by using timeout), but if the request takes a long time to finish, user might sending another request while the previous one isn't done yet, in worst case scenario this might cause the requests complete order messed up therefore the result will be invalid.

We can prevent this by aborting previous ajax request before sending a new one.
XMLHttpRequest object have abort() function we can use for that purpose, documented here:
https://developer.mozilla.org/en/XMLHttpRequest#abort

in the following jquery script, i have a 2 text fields sending ajax request onkeyup that always completed in 2 seconds and adding the inputted text in the result div, the first one will have stackable ajax request, and the other one will prevent ajax request stacking by using abort

Senin, 14 Maret 2011

cookie operation functions

function setCookie(c_name,value,exdays){
  var exdate=new Date();
  exdate.setDate(exdate.getDate() + exdays);
  var c_value=escape(value) + ((exdays==null) ? "" : "; expires="+exdate.toUTCString());
  document.cookie=c_name + "=" + c_value;
}

function getCookie(c_name){
  var i,x,y,ARRcookies=document.cookie.split(";");

  for (i=0;i<ARRcookies.length;i++){
    x=ARRcookies[i].substr(0,ARRcookies[i].indexOf("="));
    y=ARRcookies[i].substr(ARRcookies[i].indexOf("=")+1);
    x=x.replace(/^\s+|\s+$/g,"");
    if (x==c_name){
      return unescape(y);
    }
  }
}

function removeCookie(c_name){
  setCookie(c_name, '', -1);
}

Kamis, 24 Februari 2011

[shared] javascript function with flexible parameters

for example, in ruby i can write a method like this:
def certain_method *args
  "name: " + args[0..1].join(' ') + "\noccupation: " + args[2..-1].join(' ')
end

certain_method 'aya', 'hirano', 'the', 'best', 'seiyuu', 'in', 'the', 'world'
# -> "name: aya hirano\noccupation: the best seiyuu in the world"

but how to do it in javascript?

we can use function's arguments for that.
arguments object is a local variable within all functions.

You can refer to a function's arguments within the function by using the arguments object. This object contains an entry for each argument passed to the function, the first entry's index starting at 0. For example, if a function is passed three arguments, you can refer to the argument as: arguments[0], arguments[1], arguments[2]

more detailed article can be seen here:
https://developer.mozilla.org/en/JavaScript/Reference/functions_and_function_scope/arguments

ok, now for the above ruby method, the js function with equal behavior should be something like this:
function certainMethod(like, usual, you, can, define, any, parameters, here) {
    var argsArr = $(arguments).get(); // convert arguments to array
    
    return 'name: ' + argsArr.slice(0, 2).join(' ') + "\noccupation: " + argsArr.slice(2).join(' ');
    
    // first params (like) equal with arguments[0]
}

Demo

[shared] rounding closer to zero in javascript

dengan menggunakan double bitwise not operator (~~)
contoh:
var a = 1.9;
var b = -1.8;
var c = '-2.8';
var d = 'string';
var e = document;
~~a; // -> 1
~~b; // -> -1
~~c; // -> -2
~~d; // -> 0
~~e; // -> 0

operator ~~ lebih cepat dari Math.floor atau Math.ceil jadi bisa menjadi alternatif 2 fungsi tersebut..

demo:

Kamis, 17 Februari 2011

[shared] javascript deleteIf method

Background story

Di suatu project saia membutuhkan selector jquery spesial yg dapat memfilter data dari suatu element, misalkan saia ingin mengambil semua element dengan class "certain_class" dan mempunyai tooltip (menggunakan jquery.qtip) yg dapat diketahui dari adanya elem.data('qtip').

Tetapi selector data tidak dapat dilakukan (paling tidak saia tidak tahu bagaimana melakukannya), karena itu saia memutuskan untuk membuat method deleteIf untuk array dengan meniru konsep delete_if ruby.

Menggunakan method deleteIf untuk membuang element2 yg tidak mempunyai tooltip, contoh kasus diatas dapat dilakukan dengan:
$('.certain_class').deleteIf(function(){return !$(this).data('qtip');});

Code

$.fn.extend({
  deleteIf: function(fn){
    return $(this.get().deleteIf(fn));
  }
});

$.extend(Array.prototype, {
  deleteIf: function(fn){
    var newArr = [];
    for(var i=0; i < this.length; i++){
      if(!fn.call(this[i], i)) newArr.push(this[i]);
    };
    return newArr;
  }
});

Penjelasan

Method deleteIf yg pertama hanya merupakan shortcut untuk melakukan deleteIf pada jQuery object, intinya hanya merubah jQuery object menjadi Array object dengan method get() kemudian memanggil deleteIf milik Array dan merubah hasilnya kembali menjadi jQuery object.

Method deleteIf di array cukup simple, hanya meng-iterasi array dan memanggil fungsi yg di-passing dengan object yg diiterasi dan mem-push object tersebut ke array baru bila fungsi mengembalikan nilai false.

Kedua method deleteIf ini merupakan bagian dari jquery.extended_helper versi 2.5 (saat ini masi dalam pengerjaan, dan mungkin berubah sewaktu2)

Demo

Rabu, 09 Februari 2011

jsfiddle a great way to share your js code

http://jsfiddle.net

I'll also add some demo in several of my posts about js.

Post with demo included (will be updated):
- http://tech.maysora.com/2011/02/identify-object-type-in-javascript.html

Selasa, 01 Februari 2011

[shared] identify object type in javascript

UPDATE: i changed the function name to $.typeOf(obj) to prevent ambiguity with css className, please use extendedhelper 2.4b or later

untuk sebagian besar object kita dapat menggunakan typeof, contoh:
typeof object; // will return object class name in string

tetapi untuk bbrp object seperti Array, Date, window, document dll, typeof akan mengembalikan "object" bukan class yg seharusnya, untuk mengakalinya kita dapat menggunakan Object.constructor, contoh fungsi lengkapnya:

$.getClassName = function(obj){
  try{
    var constructorStr = obj.constructor.toString();
    var tempMatch = constructorStr.match(/function (.+)\(/);
    if(!tempMatch || tempMatch.length != 2) tempMatch = constructorStr.match(/ (.+)\]/);
    return tempMatch[1];
  }catch(e){
    return (typeof obj).capitalize();
  }
}

$.getClassName(1); // -> 'Number'
$.getClassName('Aya Hirano'); // -> 'String'
$.getClassName(['A','H']); // -> 'Array'
$.getClassName(null); // -> 'Object'
$.getClassName(undefined); // -> 'Undefined'
$.getClassName(new Date); // -> 'Date'
$.getClassName({'aya':'forever'}); // -> 'Object'
$.getClassName(document); // -> 'HTMLDocument'
$.getClassName(window); // -> 'Window'
$.getClassName(document.getElementsByTagName); // -> 'Function'
$.getClassName(document.getElementsByTagName('div')[0]); // -> 'HTMLDivElement'

fungsi getClassName tersebut ada di jquery.extended_helper 2.4 dan menggunakan fungsi capitalize dari js tersebut.

Demo

Rabu, 22 Desember 2010

[shared] rails render_invalid method

purpose


provide DRY solution for rendering (or redirecting) from controller on invalid request, such as failed validation on create/update

requirements


- jquery js framework
- notifications helper from my earlier post about custom ajax helper:
http://tech.maysora.com/2010/11/rails-custom-ajax-helper-for-jquery.html

codes


application_controller.rb
# render invalid request to show error message depend on request type
# example:
#   render_invalid "Update failed."
#     ajax: show error notification with message "Update failed."
#     html: simply render text "Update failed."
#   render_invalid "Save failed.", :render => {:action => "new"}
#     ajax: show error notification with message "Save failed."
#     html: render action new with "Save failed." flash message
#   render_invalid "Save failed.", :render => {:action => "new"}, :flash => false
#     same as above but without flash for html
#   render_invalid "You can't do that.", :redirect_to => {:action => "index"}, :flash => false, :status => :unauthorized
#     same as above but redirecting to index instead of rendering new for html
#     and using unauthorized(401) status instead of not_acceptable(406)
#   render_invalid "Update failed.", :object => @user, :fields_wrapping => true
#     ajax: show error notification with addition of object's error messages and wrap error fields with div class fieldWithErrors
#     html: same as ajax minus fields wrapping
def render_invalid message="Error.", options={:flash => true}
  options[:status] ||= :not_acceptable
  message += "
#{options[:object].errors.full_messages.join(' ')}
" if options[:object].present? respond_to do |format| format.js do render(:update, :status => options[:status]) do |page| page << notification_error(message) if options[:object].present? && options[:fields_wrapping] page.rjs_clean_error_fields request page.rjs_apply_error_fields options[:object], request end end end format.html do if options[:render].present? flash.now[:error] = message if options[:flash] render options[:render].try(:merge, {:status => options[:status]}) || {:text => message, :status => options[:status]} elsif options[:redirect_to].present? flash[:error] = message if options[:flash] redirect_to options[:redirect_to] else render :text => message, :status => options[:status] end end end end

application_helper.rb (only if you use ajax error fields wrapping)
def rjs_apply_error_fields object, request
  klass_name = object.class.name.underscore
  wrapper = "
" object.errors.each do |attr, msg| attr = attr.split(".") page << "var $fields = $(), $container = $('\##{klass_name}_#{object.id || "new"}');" page << "if(!$container.length) $container = $('form[action=\\'#{request.request_uri}\\']');" page << "if(!$container.length) $container = $(document);" if attr.length > 1 object.send(attr.first).each_with_index do |x,i| page << "$fields = $fields.add('[id^=#{klass_name}_#{attr.first}_attributes][id$=#{attr.last}]:eq(#{i}),label[for^=#{klass_name}_#{attr.first}_attributes][for$=#{attr.last}]:eq(#{i})', $container)" if x.errors.on(attr.last) end else page << "$fields = $('label[for=#{klass_name}_#{attr}],\##{klass_name}_#{attr}', $container);" end page << "if(!$fields.parent('.fieldWithErrors').length)" page << "$fields.wrap('#{wrapper}')" end end def rjs_clean_error_fields request page << "$('.fieldWithErrors.rjs_#{request.request_method}_#{request.request_uri.gsub(/_=\d+($|&)/, "").parameterize} *').unwrap();" end

usage

users_controller.rb (using non ajax form)
#...

def create
  @user = User.new(params[:user])
  unless @user.save
    render_invalid "Failed to register.", :object => @user, :render => {:action => "new"}
  else
    flash[:info] = "Registration success, please check your email to activate."
    redirect_to login_path
  end
end

#...
users_controller.rb (using ajax form)
#...

def update
  @user = current_user
  unless @user.update_attributes(params[:user])
    render_invalid "Profile update failed.", :object => @user, :fields_wrapping => true
  end
end

#...
and update.js.rjs (for success behavior)
page << notification_info "Profile updated."
page.rjs_clean_error_fields request # used for cleaning error fields wrapping
#... other necessary action ...

additional tips

depending on your site style, it might be a good idea to change the div wrapper in rjs_apply_error_fields helper to span. you can also change rails standard error field wrapper for non-ajax form, see this post

[shared] floating point error in js

console.log(0.1+0.2); //-> 0.30000000000000004
console.log(0.1*0.2); //-> 0.020000000000000004

solution:
1. use x.toFixed(n) if you only need 5 or less decimal digit
2. use function from http://www.codingforums.com/showpost.php?p=483962&postcount=9
3. best solution, use js BigDecimal library https://github.com/jhs/bigdecimal.js or http://stz-ida.de/index.php?option=com_content&view=article&id=18&Itemid=32

summary:
use number 1 if decimal precision isn't really important (most likely), or use number 3 with some performance cost

Selasa, 19 Oktober 2010

jQuery ExtendedHelper

http://code.google.com/p/jquery-extendedhelper/

jQuery hash merge

merge hash2 to hash1:

$.extend(hash1, hash2);


for recursive merge:

$.extend(true, hash1, hash2);

Senin, 18 Oktober 2010

add javascript files to rails default

environment.rb

# reset:
ActionView::Helpers::AssetTagHelper::JAVASCRIPT_DEFAULT_SOURCES = [
'jquery-1.4.3', 'jquery-ui-1.8.6.custom', 'jrails',
'jquery.extended_helper_2.0', 'date-id', 'jquery.gritter'
]
ActionView::Helpers::AssetTagHelper::reset_javascript_include_default

# adding:
ActionView::Helpers::AssetTagHelper::register_javascript_include_default 'extended_helper', 'date'

Kamis, 30 September 2010

[shared] datejs, impressive js date helpers

http://www.datejs.com/
http://code.google.com/p/datejs/wiki/APIDocumentation

Rabu, 04 Agustus 2010

[shared] javascript arrayCompact

di RoR saia biasa menggunakan array.compact untuk memastikan di dalam suatu array tidak terdapat elemen nil. masalahnya javascript tidak mempunyai fungsi tersebut, untuk itu saia membuat fungsi kecil untuk melakukan hal td + sedikit tambahan untuk membuang bukan hanya null tetapi jg string kosong atau false:


/* return a copy of array with all null and optionally blank or false elements removed
* example:
* - arrayCompact(['a',null, 0, '', false]) //=> [ 'a', 0, '', false ] (only null removed)
* - arrayCompact(['a',null, 0, '', false], 1) //=> [ 'a', 0, false ] (only null and empty string removed)
* - arrayCompact(['a',null, 0, '', false], 2) //=> [ 'a' ] (all that equal to false removed)
*/
function arrayCompact(array, remove_level){
var new_array = new Array();
for(k in array)
if(typeof(array[k]) != 'undefined' &&
array[k] != null &&
!(remove_level > 0 &&
(typeof(array[k]) == 'string' || remove_level > 1) &&
array[k] == ''))
new_array.push(array[k]);
return new_array;
}


fungsi tersebut menggunakan plain javascript jadi dapat digunakan tanpa js framework apapun

[shared] Simple jQuery function for form debugging

dalam developing site, seringkali saia membuat form dengan elemen2 seperti select tag, radio button, input hidden dll. normalnya user hanya dapat memasukkan value2 yang sudah saia tentukan pada elemen2 tersebut, tp user yg "iseng" bisa saja memasukkan value di luar value2 yg sudah ditentukan td sehingga dapat merusak aplikasi.

buat mencegah saia harus membuat server side validation yang seringkali terlupakan karena pada saat saia melakukan testing saia melakukannya sebagai user normal bukan sebagai user "iseng". hal tersebut membuat saia berpikir untuk membuat script sederhana untuk mempermudah testing.

menggunakan jQuery ternyata script helper nya cukup simple:

function debugInput(){
$('.debug_input').remove();
$(':input:not(textarea,:text:not(.date),:button,:submit,:reset)').each(function(){
$(this).after('
'+this.name+':
').css('border', 'medium dotted gray');
});
$('form').each(function(){
$(this).append('
'+this.id+':
')
});
$('.debug_input').css('opacity', 0.7).draggable();
}


pada saat dijalankan fungsi tersebut akan menambahkan input text normal di tiap elemen2 form sehingga saia dapat dengan mudah memasukkan value2 aneh di select tag dan sejenisnya. selain itu fungsi ini jg menambahkan tombol submit di form.

contoh pemakaian pada site php manual:

Kamis, 04 Maret 2010

[shared] jQuery serialize only a part of form

baru2 ini di project yang sedang saia kerjakan, saia menemui suatu masalah, intinya saia ingin men-submit suatu bagian dari form yang cukup besar menggunakan ajax untuk mendapatkan nilai yang akan dipakai oleh bagian lain dari form.

secara sederhana kode nya sebagai berikut:

<form>
<input type="text" name="user_name" />
<div id="part1">
<input type="text" name="user_paid_amount" onchange="ajaxRequestToGetPart2ValueBasedOnPart1();" />
<input type="text" name="user_type" onchange="ajaxRequestToGetPart2ValueBasedOnPart1();"/>
</div>
<div id="part2">
<input type="text" name="price_amount" value="" />
</div>
</form>

intinya ketika ada perubahan pada input2 di part1, part2 akan secara otomatis diisi.

namun yang jadi masalah adalah bagaimana cara mengambil value2 dari part1 untuk dikirimkan sebagai parameter..
jquery menyediakan fungsi serialize untuk mengambil semua element dari suatu form (http://api.jquery.com/serialize/), sayangnya hal tersebut tidak dapat dilakukan untuk container selain form, jadi $('#part1').serialize() tidak dapat digunakan..

Solusi 1 - clone(true)

jQuery Serialize a Fieldset
menggunakan clone untuk meng-copy isi div yang kemudian diletakkan pada form yang disembunyikan. form tersebut lalu di-serialize kemudian di-remove.

namun clone merupakan fungsi yang cukup memakan resource.

Solusi 2 - selector

solusi yang ke-2 dengan menggunakan selector :input. kira2 kodenya sebagai berikut:

function serialize(container){
//container: string selector or element of the container
$(':input', container).serialize();
}
serialize('#part1');

lebih simple dan efektif :)