﻿/*
This file is designed to impletement utilies in common scenario of varies pages.
*/

var _DESIGN = null;
var _DEBUG = null;

function debug(alertMsg) {
    if (_DEBUG != null) {
        alert(alertMsg);
    }
}

// date picker parameters;
var g_DatePickerParas = {
    closeText: '关闭',
    prevText: '&#x3c;上月',
    nextText: '下月&#x3e;',
    currentText: '今天',
    monthNames: ['一月', '二月', '三月', '四月', '五月', '六月',
		'七月', '八月', '九月', '十月', '十一月', '十二月'],
    monthNamesShort: ['一', '二', '三', '四', '五', '六',
		'七', '八', '九', '十', '十一', '十二'],
    dayNames: ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'],
    dayNamesShort: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],
    dayNamesMin: ['日', '一', '二', '三', '四', '五', '六'],
    weekHeader: '周',
    dateFormat: 'yy-mm-dd',
    firstDay: 1,
    isRTL: false,
    showMonthAfterYear: true,
    yearSuffix: '年'
};

// clear content of target div specified by name, and start to loading.
function ShowLoading(div, width, height) {
    $(div).html("<div class='loading'><img src='/images/loading.gif'></img></div>");
    var loadingDiv = $(div + " .loading");

    var widthOffset = (width == null ? $(div).width() : width) - 16;
    var heightOffset = (height == null ? $(div).height() : height) - 16;
    loadingDiv.css("float", "left").css("margin-left", widthOffset / 2.0).css("margin-top", heightOffset / 2.0);
}

// show loading under a jQueryObject;
function jShowLoading(jDiv) {
    jDiv.html("<div class='loading'><img src='/images/loading.gif'></img></div>");
    var loadingDiv = jDiv.find(" .loading");
    var widthOffset = jDiv.width() - 16;
    var heightOffset = jDiv.height() - 16;
    loadingDiv.css("float", "left").css("margin-left", widthOffset / 2.0).css("margin-top", heightOffset / 2.0);
}

// stop loading animation, and clear the div specified by names
function HideLoading(div) {
    $(div).html("");
}

// stop loading animation under JQuery object;
function jHideLoading(jDiv) {
    jDiv.html("");
}

function OpenMailWin(tripId) {
    iT = (screen.availHeight - 248) / 2 - 15;
    iL = (screen.availWidth - 375) / 2 - 3;
    window.open("/mailit.aspx?q=" + tripId, "newwindow", "height=248, width=375,top=" + iT + ",left=" + iL + ",toolbar=no, menubar=no, scrollbars=no, resizable=no, location=no, status=no");
}

// show error msg when ajax communication failed.
function ShowAjaxError(div) {
    $(div).html("<span class='ajaxError'>请稍候再试</span>");
}

// show error msg when ajax communication failed.
function jShowAjaxError(jDiv) {
    jDiv.html("<span class='ajaxError'>请稍候再试</span>");
}


function ShowAjaxNoResult(div) {
    $(div).html("<span class='ajaxNoResult'>没有找到符合搜索条件的记录</span>");
}

// auto check anchor by 100 miliseconds, callback function is triggered when anchor changed;
function InitAnchor(callback) {
    setInterval(function () {
        if (curAnchor != window.location.hash) {
            curAnchor = window.location.hash;
            if (callback != null) {
                callback();
            }
        }
    }, 100);
}

// set anchor value;
// input parameter valObj is a type of json object;
function SetAnchor(valObj) {

    //serialize json object;
    var serialized = "#";

    var i = 0;
    for (key in valObj) {
        serialized += key + "=" + valObj[key];
        if (i != $(valObj).length - 1) {
            serialized += "&";
        }

        i = i + 1;
    };

    // update anchor;
    curAnchor = serialized;
    window.location.hash = curAnchor;
}

function GetAnchor() {
    if (curAnchor != null) {
        // anchor string retrieved from window.location.hash is in style of "#..."
        return GetUrlParameter("tk", curAnchor);
    }
    else {
        return curAnchor;
    }
}

/**************************************************************************************************

NAME: URI   

DESCRIPTION:
Provides functionality to access key value pairs browser URLs from address bar.
It contains:
1. Retrieves values by specified key in URL.
2. Writes back value into URLs in address bar by specified key.

**************************************************************************************************/
var URI = {

    /**********************************************************************************************

    NAME: Get

    DESCRIPTION:
    Retrieves a value from URL by specified key. The URL contains zero or more key value pairs with
    the format of "http://travel.msra.cn/?key1=val1#key2=val2...". Note that, anchor part after '#'
    is also eligible.

    PARAMS:        
    -key        [String]
    A string value which indicates input specified key
        
    -context    [String]
    

    RETURNS:    [String]
        
    **********************************************************************************************/
    Get: function (key, context) {

        // escpase characters in key, which changes '[',']' to  '\[','\]'.
        // otherwise, characters will be regcognized as reserved ones in regular expression.
        key = key.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");

        if (context == null) {
            context = window.location.hash;
        }

        var regexS = "[\\?&#]" + key + "=([^&#]*)";
        var regex = new RegExp(regexS);
        var results = regex.exec(context);
        if (results == null) {
            return null;
        }
        else {
            return results[1];
        }
    },

    /**********************************************************************************************
    
    NAME:   Set
    
    DESCRIPTION:
    set     
    
    **********************************************************************************************/
    Set: function (key, val) {
        var oldVal = this.Get(key, this._bufferedAnchor);
        if (oldVal == val) {
            return;
        }

        key = key.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
        var regexS = key + "=([^&#]*)";
        var regex = new RegExp(regexS);

        var context = this._bufferedAnchor;

        var replaced = context.replace(regex, key + "=" + val);

        if (oldVal == null) {
            if (context == null || context.length < 1 || (context.length == 1 && context[0] == '#')) {
                context = "#" + key + "=" + val;
            }
            else {
                context += "&" + key + "=" + val;
            }
        }
        else {
            context = replaced;
        }

        this._bufferedAnchor = context;
    },

    // current sharing string kept in memory;
    _curAnchor: null,

    // a buffer to contain changes on window.location.hash, but not triggered events untill Push() is invoked;
    _bufferedAnchor: "",

    // monitors the changes of window.location.hash, which trigered URI changed events;
    Init: function (changed) {
        setInterval(function () {
            if (this._curAnchor != window.location.hash) {
                this._curAnchor = window.location.hash;
                if (changed != null) {
                    changed();
                }
            }
        }, 100);
    },

    // update URL function from buffered URL, which makes us to set URLs as many times as we could 
    // WITHOUT triggered URL changed events untill Push() function;
    Push: function () {
        window.location.hash = this._bufferedAnchor;
    }
}

/**************************************************************************************************

NAME:   LoadDataAsync

DESCRIPTION:
General function to retrieve html blocks from server page "get.aspx". It uses POST method to reqest
data if parameters are specified, otherwise it's GET by default.

All ajax access functions loading data can only invoke this general method to retrieve data.

Besides that, it,
1. set loading effect when ajax requesting;
2. load response data into specified container DOM if success with data returned;
3. show prompt information if failed in network or success with data NOT FOUND;

PARAS:
- dataContainer [DOM or String]
Specified a dom object or dom string, which is a root container for loading effect and response
data

- paras         [JSON Object]
Parameters passed into ajax service inoke, it contains at least "act", which is function type

- callback      [Function Pointer]
Optional. Callback function is invoked at end of data returned. It returnes responseText and
textStatus; 

RETURNS:    None

**************************************************************************************************/
function LoadDataAsync(dataContainer, paras, callback) {

    ShowLoading(dataContainer);

    $(dataContainer).load("/get.aspx", paras, function (responseText, textStatus, XMLHttpRequest) {

        if (textStatus == "success") {
            //show ajax information if no reults found;
            if (responseText == "") {
                ShowAjaxNoResult(dataContainer);
            }
        }
        else {
            ShowAjaxError(dataContainer);
        }

        if (callback != null) {
            callback(responseText, textStatus);
        }
    });
}

/**************************************************************************************************

NAME:   Pager

DESCRIPTION:
A UI control to turn over pages of results list. Typically, it contains buttons which are type of
1. Previous Page Button. It turns page index to previous page unless it's already first one.
2. Next Page Button. It turns page index to next page unless it's already last one.
3. Page Index Buttons. It turns page index to specified page index.

PARAS:
- divContainer  Object
It's a javascript object which is
1. a div element.
2. container to host all display elements of this page control.

- maxPageNo     int

- validLen  int
Max distance of elements in valid range with current page number.Numbers which falls out of valid 
ranges will turn to eclipse except first and last number.

**************************************************************************************************/
function Pager(divContainer, minPageNo, maxPageNo, validLen) {

    // it's holding a THIS pointer to pager instance, due to javascript OOP limitation described in http://w3future.com/html/stories/callbacks.xml
    // which is, in callback or event handler function, javascript cannot recognize "this" keyword as class instance but a DOM elevent.
    // using me as a alternative keywords to this.
    var me = this;

    this.MaxPageNo = maxPageNo;
    this.MinPageNo = minPageNo;
    this.CurPage = -1;
    this._ValidLen = validLen;
    this._Container = divContainer;
    this.PageChanged = null;

    this.TurnTo = function (pageNo) {

        // validate input page number into correct page range;
        if (pageNo < this.MinPageNo) {
            pageNo = this.MinPageNo;
        }
        else if (pageNo > this.MaxPageNo) {
            pageNo = this.MaxPageNo;
        }

        // do nothing if it's THE CURRENT page already.
        if (pageNo == this.CurPage) {
            return;
        }

        // update page related fields in object;
        this.CurPage = pageNo;

        // render all controls once page index changed;
        this._Render(this._Container, this.MinPageNo, this.MaxPageNo, this.CurPage, this._ValidLen);

        // fire event handlers for page index change;
        if (this.PageChanged != null) {
            this.PageChanged();
        }
    }

    this._Render = function (div, start, end, cur, vLen) {

        // clear the target div.
        $(div).html("");

        // for invalid arguments 
        if (start > end || cur < start || cur > end || div == null || vLen < 0) {
            return;
        }

        // add previous page button if not first page.
        if (cur != start) {
            var prePage = $("<a class='pageButton'>上一页</a>");
            prePage.click(function () {
                me.TurnTo(me.CurPage - 1);
            })
            $(div).append(prePage);
        }

        var validStart = cur - vLen;
        var validEnd = cur + vLen;

        // add page index buttons, which are specified by numbers.
        for (var i = start; i <= end; i++) {

            // if only 1 page and index is 1, ignore it;
            if (i == start && start == end && i == cur && i == 1) {
                continue;
            }

            if (i == cur) {
                // display current page button without click event handlers
                var curBtn = $("<span class='curPageButton'>" + i + "</span>");
                $(div).append(curBtn);
                continue;
            }

            // i!= cur;

            // append left eclipse
            if (i > start && i < validStart) {
                if (i == start + 1) {
                    $(div).append("...");
                }
                continue;
            }

            // append right eclipse
            if (i < end && i > validEnd) {
                if (i == end - 1) {
                    $(div).append("...");
                }
                continue;
            }

            // display normal buttons
            var normalBtn = $("<a class='pageButton'>" + i + "</a>");
            normalBtn.click(function () {

                // cannot use this.TurnTo(...) since "this" cannot be recognized as class instance correctly;
                // cannot use me.TurnTo(i) since "i" cannot be kept as local value i, but the last appeared value (MaxPageNo).
                // cannot use me.TurnTo($(this).text()), since TurnTo only accept int type number. When a string value is passed in,
                //      there will be expcetion or underlying bugs. Here is an example that,
                //      If we have a variable X definded inside me.TurnTo function, and accept the input value as "X=<input paras>;",
                //      as a disaster that, we can find "X+1" turns to be a string join not a add function.
                me.TurnTo(parseInt($(this).text()));

            })
            $(div).append(normalBtn);
        }

        // add next page button if it's not last one;
        if (cur != end) {
            var nextPage = $("<a class='pageButton'>下一页</a>");
            nextPage.click(function () {
                me.TurnTo(me.CurPage + 1);
            })
            $(div).append(nextPage);
        }
    }
}

/**************************************************************************************************

NAME: Filters   

DESCRIPTION:
Provides filter function to filter unreasonable characters 

**************************************************************************************************/
var Filters =
{
    FilterSearch: function (keywords) {
        if (keywords == null) {
            return keywords;
        }
        else {

            var filtered = ToCDB(keywords.toString());

            if (filtered.length > 50) {

                // get the left 100 chars;
                filtered = filtered.substr(0, 50);
            }

            //delete the blank area of the head and the title of the input
            var reg0 = /^\s*|\s*$/g;
            filtered = filtered.replace(reg0, "");

            //merge blank area
            var reg1 = /\s{2,}/g;
            filtered = filtered.replace(reg1, " ");


            //filter special character
            var reg2 = /[\?!\^~\\@#\$%\^\&\*\(\)_\+\{\}\|\:<>`\-\=\[\]\;,\.\/\"\']/g;
            filtered = filtered.replace(reg2, "");

            var reg3 = /[？！……～、￥×（）——『』：《》 【】；。”“’‘]/g;
            filtered = filtered.replace(reg3, "");

            return filtered;
        }
    },

    // filter input query from search box
    Filter: function (keywords) {
        if (keywords == null) {
            return keywords;
        }
        else {

            var filtered = ToCDB(keywords.toString());
            if (filtered.length > 20) {

                // get the left 100 chars;
                filtered = filtered.substr(0, 20);
            }

            //delete the blank area of the head and the title of the input
            //            var reg0 = /^\s*|\s*$/g;
            //            filtered = filtered.replace(reg0, "");

            //filter special character
            var reg2 = /[\?!\^~\\@#\$%\^\&\*\(\)_\+\{\}\|\:<>`\-\=\[\]\;,\.\/\"\']/g;
            filtered = filtered.replace(reg2, "");

            // commented by xiquan, cauze not needed in input
            //            var reg3 = /[\？！\……～\、@#\￥%\@\×\（\）——\+\『\』\|\：《》`\-\=\【\】\；。\”\“\’\‘\/]/g;
            //            filtered = filtered.replace(reg3, "");
            //            alert(filtered);
            return filtered;
        }
    }
}


/**************************************************************************************************

NAME: ToCDB

DESCRIPTION:
This function turns the code of char in a string from full angle to half angle; 
The full angle code of space is 12288, and the half angle code of sapce is 32;
For other chars which half angle code is from 33-126, there full angle code is between 65281-65374,
the difference between them is 65248

PARAS:
-str        [String]
A string value which need to be convented from full angle style to half angle style

RETURNS:    [String]

**************************************************************************************************/
function ToCDB(str) {
    var tmp = "";
    for (var i = 0; i < str.length; i++) {
        if (str.charCodeAt(i) > 65248 && str.charCodeAt(i) < 65375) {
            tmp += String.fromCharCode(str.charCodeAt(i) - 65248);
        }
        else {
            if (str.charCodeAt(i) == 12288) {
                tmp += String.fromCharCode(32);
            }
            else {
                tmp += String.fromCharCode(str.charCodeAt(i));
            }
        }
    }
    return tmp;
}

if (typeof XMLHttpRequest == "undefined")
    XMLHttpRequest = function () {
        return new ActiveXObject(
            navigator.userAgent.indexOf("MSIE 5") >= 0 ?
            "Microsoft.XMLHTTP" : "Msxml2.XMLHTTP"
        );
    };

function GetAjaxValue(url) {
    var xml = new XMLHttpRequest();
    xml.open("POST", url, true);   //True to asyn mode
    xml.send(null);
    return xml.responseText;
}

function Log(entryType, query, where, description) {

    try {
        var sScreen = "";
        try {
            sScreen = window.screen.width + "x" + window.screen.height;
        }
        catch (ex) {
            sScreen = "0x0";
        }
        var LoggingLink = "/logging.aspx?EntryType=" + entryType +
        "&query=" + HttpUitility.encode("" + query) +
        "&where=" + HttpUitility.encode("" + where) +
        "&desc=" + HttpUitility.encode("" + description) +
        "&From=" + HttpUitility.encode("" + window.location) +
        "&Referrer=" + HttpUitility.encode("" + document.referrer) +
        "&screen=" + sScreen;
        GetAjaxValue(LoggingLink);
    }
    catch (ex) { }
}

var HttpUitility = {

    // public method for url encoding
    encode: function (string) {
        return escape(this._utf8_encode(string));
    },

    // public method for url decoding
    decode: function (string) {
        return this._utf8_decode(unescape(string));
    },

    // private method for UTF-8 encoding
    _utf8_encode: function (string) {
        string = string.replace(/\r\n/g, "\n");
        var utftext = "";
        for (var n = 0; n < string.length; n++) {
            var c = string.charCodeAt(n);
            if (c < 128) {
                utftext += String.fromCharCode(c);
            }
            else if ((c > 127) && (c < 2048)) {
                utftext += String.fromCharCode((c >> 6) | 192);
                utftext += String.fromCharCode((c & 63) | 128);
            }
            else {
                utftext += String.fromCharCode((c >> 12) | 224);
                utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                utftext += String.fromCharCode((c & 63) | 128);
            }
        }
        return utftext;
    },

    _utf8_decode: function (utftext) {
        var string = "";
        var i = 0;
        var c = c1 = c2 = 0;

        while (i < utftext.length) {
            c = utftext.charCodeAt(i);
            if (c < 128) {
                string += String.fromCharCode(c);
                i++;
            }
            else if ((c > 191) && (c < 224)) {
                c2 = utftext.charCodeAt(i + 1);
                string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
                i += 2;
            }
            else {
                c2 = utftext.charCodeAt(i + 1);
                c3 = utftext.charCodeAt(i + 2);
                string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
                i += 3;
            }
        }
        return string;
    }
}

// authentication state from server;
var g_SvrLiveState;

function InitLiveApp(channelUrl, callbackUrl, clientId) {
    // get json object of live settings;
    var liveAppSetting = $.ajax({ url: "/getlive.aspx", async: false }).responseText;

    var jLive = $.parseJSON(liveAppSetting);

    g_SvrLiveState = jLive.svrauth;

    // load live app tag and initialize it;

    Microsoft.Live.Core.Loader.onLoad(function () {
        Microsoft.Live.App.initialize(jLive.app);
    });
}

// Callback for when the Application is loaded.
function appLoaded(evtArgs) {

    //Microsoft.Live.Core.Namespace.using("wl:Microsoft.Live");

    // get json object of live settings;
    var liveAppSetting = $.ajax({ url: "/getlive.aspx", type: "POST", async: false }).responseText;
    var jLive = eval("(" + liveAppSetting + ")");
    //alert(jLive.svrauth);
    g_SvrLiveState = jLive.svrauth;

    var auth = Microsoft.Live.App.get_auth();
    //alert(liveAppSetting);
    auth.set_callbackUrl(jLive.app.callbackurl);


    // check token value to indicate if live signIn control will sign in automatically.
    var token = auth.get_accessToken();
    if (typeof token == "undefined" || token == null || token == "") {
        // sign in control will not sign in itself, since there is no token value;
        g_IsDefaultSignIn = false;
        SyncAuth(evtArgs);
    }
    else {
        // if toke exists, live will TRY to sign in automatically. set global switch to true and check the first load in
        // signInCompleted methods.
        g_IsDefaultSignIn = true;
    }
}

var g_IsDefaultSignIn = true;

// Callback for when a Live user signs in.
function signInCompleted(signInCompletedEventArgs) {

    //alert(g_IsDefaultSignIn);
    // Sync live state with servers only at first sign in with is page loaded time;
    // Cannot put it at appLoaded, since signIn is not called at that time;
    if (g_IsDefaultSignIn) {
        g_IsDefaultSignIn = false;
        SyncAuth(signInCompletedEventArgs);
        return;
    }

    //alert("second sign in");

    if (signInCompletedEventArgs.get_resultCode() != Microsoft.Live.AsyncResultCode.success) {
        //alert("error: " + signInCompletedEventArgs.get_error() + signInCompletedEventArgs.get_resultCode());
        return;
    }

    // Client success in sign in. And also we can believe that servers are correctly authenticated in session.
    g_SvrLiveState = "authenticated";

    if (typeof UpdateAuthUI != "undefined") {
        UpdateAuthUI(true);
    }

}

function signOutCompleted() {

    // refresh page to clear server session to log out;
    signOutServer();
}

function signOutServer() {
    //alert("sign out redirect");
    window.location.href = "/signout.aspx?returnurl=" + encodeURIComponent(window.location.href);
}

// a cached live user name;
var g_LiveUserName = "";

function NameConvert(a) {
    g_LiveUserName = a;
    return a ? a : String.Empty;
}

/**************************************************************************************************

NAME:   SyncAuth

Description:
Synchronize authentication of clients and server side. There are 4 different cases to check. This
method will be invoked only when first app sign in completed to check authentication.

After the check, all auth related variables will be updated.

PARAS:  
- evtArgs [Microsoft.Live.ApplicationLoadCompletedEventArgs]

- updateAuthUICallback [callabck(bool isAuthenticated)]
call back function when authentication is valid to update UI;

RETURNS:    None

**************************************************************************************************/
function SyncAuth(evtArgs, updateAuthUICallback) {
    var app = Microsoft.Live.App;
    var appContext = evtArgs.get_dataContext();
    var auth = Microsoft.Live.App.get_auth();

    var clientAuthenticated = (auth.get_state() == Microsoft.Live.AuthState.authenticated);
    var serverAuthenticated = (g_SvrLiveState == "authenticated");

    //alert(auth.get_state());
    if (clientAuthenticated) {
        //alert(serverAuthenticated);
        if (serverAuthenticated) {
            // it's a SUCCESS scenario that both client and servers are signed in;
            if (typeof UpdateAuthUI != "undefined") {
                UpdateAuthUI(true);
            }
        }
        else {
            // invalid state since servers are not signed in;
            // sign out and try to sign in agian;
            Microsoft.Live.App.signOut();
            auth.set_accessToken();
            //$.cookie("wl_internalState", null);
            try {
                var exp = new Date();
                exp.setTime(exp.getTime() - 1);
                document.cookie = "wl_internalState=;expires=" + exp.toGMTString();
            }
            catch (ex) {
                // keep silence if cookie failed;
            }

            signOutServer();
        }
    }
    else {
        if (serverAuthenticated) {
            // sersers are not refreshed as the up-to-date sign in status;
            // in this case client has been already signed out;
            // update server to sync state of live;                       
            signOutServer();
        }
        else {
            // it's a SUCCESS check scenario that both client and servers are signed out;
            if (typeof UpdateAuthUI != "undefined") {
                UpdateAuthUI(false);
            }
        }
    }
}


/**************************************************************************************************

NAME:   RequestAsync

DESCRIPTION:
Access FrontJs.svc asynchronizlly, and invoke callback with reponse data from service;
This method will be called by other service access methods.

PARAS:
- methodName    String
Calling method to access data from FrontJs.svc

- requestData   String
String value with format of JSON types;

- responseContainer     jQuery Object
Container elements for loading in requesting data, and errors prompting;

- callback  function(Object)
Callback function to response with data object.

RETURNS:    None

**************************************************************************************************/
function RequestAsync(methodName, requestData, responseContainer, callback) {
   
    //alert(requestData);
    $.ajax(
    {
        type: 'post',
        url: '/FrontJs.svc/' + methodName,
        contentType: 'text/json',
        data: requestData,
        dataType: 'json',

        beforeSend: function (XMLHttpRequest) {
            if (responseContainer != null) {
                jShowLoading(responseContainer);
            }
        },

        success: function (data, textStatus) {
          
            if (responseContainer != null) {
                jHideLoading(responseContainer);
            }

            // if the server is not authenticated, redirect to authentication;
            if (data.d.State == 1) {
                signOutServer();
                return;
            }

            // if it's success state;
            if (callback != null) {
                callback(data.d.Response);
            }
        },

        complete: function (XMLHttpRequest, textStatus) {
         
            // cannot use HideLoading here, since complete event will be triggered last,
            // which will clear all data in result div.
            //            alert(textStatus);
            //            alert(XMLHttpRequest.statusText);
            //            alert(XMLHttpRequest.responseText);
        },

        error: function (XMLHttpRequest, textStatus, errorThrown) {
    
            //alert(textStatus);
            //alert(XMLHttpRequest.statusText);
            //alert(XMLHttpRequest.responseText);
            // show error msg in display blocks.
            if (responseContainer != null) {
                jShowAjaxError(responseContainer);
            }
        }
    })
}


function Validate(actions) {
    // if no rulste, pass;
    if (actions == null || $(actions).length == 0) {
        return true;
    }

    $(".error").remove();

    var re = true;
    $(actions).each(function (i, action) {
        if (!ValidateRule(action)) {
            $("<div class='error'>" + action.message + "</div>").insertAfter(action.target);
            re = false;
        }
    })

    return re;
}

function ValidateRule(action) {
    if (action == null) {
        return false;
    }

    var val = $(action.target).val();

    // required;
    if (action.rule == "required") {
        if (val == null || val == "" || (val.length > action.maxLen && action.maxLen != null)) {
            return false;
        }
    }
    else if (action.rule == "optional") {
        if (val != null && val != "" && action.maxLen != null && val.length > action.maxLen) {
            return false;
        }
    }
    else if (action.rule == "url") {
        var g = /^[a-zA-z]+:\/\/[^\s]*$/;
        if (val != null && val != "" && g.exec(val) == null) {
            return false;
        }
    }
    else if (action.rule == "date") {
        // in form of "2010-08-12";
        var g = /^((((1[6-9]|[2-9]\d)\d{2})-(0[13578]|1[02])-(0[1-9]|[12]\d|3[01]))|(((1[6-9]|[2-9]\d)\d{2})-(0[13456789]|1[012])-(0[1-9]|[12]\d|30))|(((1[6-9]|[2-9]\d)\d{2})-02-(0[1-9]|1\d|2[0-8]))|(((1[6-9]|[2-9]\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))-02-29))$/;

        if (val != null && val != "" && g.exec(val) == null) {
            return false;
        }
    }

    // check types;
    if (action.type == "double") {
        var g = /^((\d+\.\d{0,2})|(0\.\d{0,2})|(\d+)|(\d))$/;
        var fVal = parseFloat(val);
        if (g.exec(val) == null || isNaN(fVal) || fVal < action.min || fVal > action.max) {
            return false;
        }
    }
    else if (action.type == "mail") {
        var g = /^(([ _a-z0-9- ]+(\.[a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3}));?){0,10}$/;
        if (g.exec(val) == null) {
            return false;
        }
    }
    else if (action.type == "title") {
        var g = /^([\u4e00-\u9fa5]|[A-Za-z0-9 ])*$/;
        if (g.exec(val) == null) {
            return false;
        }
    }

    return true;
}

function LoadJsFile(file) {
    head = document.getElementsByTagName('head').item(0);
    script = document.createElement('script');
    script.src = file;
    script.type = 'text/javascript';
    script.defer = true;
    void (head.appendChild(script));
}

/**************************************************************************************************

NAME:   MaximizeToPageSize

DESCRIPTION:
set the size of the  container to full visible area in the screen according to the browser

PARAS:  div
the container that should be sized

RETURNS:    None

**************************************************************************************************/

function MaximizeToPageSize(div) {
    var scrW, scrH;

    if (window.innerHeight && window.scrollMaxY) {    // Mozilla    
        scrW = window.innerWidth + window.scrollMaxX;
        scrH = window.innerHeight + window.scrollMaxY;
    }
    else if (document.body.scrollHeight > document.body.offsetHeight) {    // all but IE Mac    
        scrW = document.body.scrollWidth;
        scrH = document.body.scrollHeight;
    }
    else if (document.body) { // IE Mac    
        scrW = document.body.offsetWidth;
        scrH = document.body.offsetHeight;
    }

    var winW, winH;
    if (window.innerHeight) { // all except IE    
        winW = window.innerWidth;
        winH = window.innerHeight;
    }
    else if (document.documentElement && document.documentElement.clientHeight) {    // IE 6 Strict Mode   
        winW = document.documentElement.clientWidth;
        winH = document.documentElement.clientHeight;
    }
    else if (document.body) { // other    
        winW = document.body.clientWidth;
        winH = document.body.clientHeight;
    }    // for small pages with total size less then the viewport  
    var pageW = (scrW < winW) ? winW : scrW;
    var pageH = (scrH < winH) ? winH : scrH;

    $(div).css({ "width": pageW });
    $(div).css({ "height": pageH });
}

/**************************************************************************************************

NAME:   Popup

DESCRIPTION:
Utility to popup window in jQuery object. It supports modal windows popup.

**************************************************************************************************/
var Popup =
{
    /**********************************************************************************************

    NAME: ShowModal

    DESCRIPTION:
    Function to display a modal window, based on jQuery object, which containes a closing button. 
    After use this method, users only need to add contents into returned jQuery objects.

    PARAS:
    - divContainer  jQuery object
    Parent jQuery object as a root container of the popup elements, in which the popup contents
    will be put.

    RETURNS:
    jQuery object. It returns a content div, in forms of jQuery object.

    **********************************************************************************************/
    ShowModal: function (divContainer) {
        // check parent container;
        if (typeof divContainer == "undefined" || divContainer == null) {
            return null;
        }

        // close all popup window first;
        this.Close();

        // add popup background to filter keyword events;
        divContainer.append("<div class='popBackground' style='filter: alpha(opacity=90); display: block; height: 2417px;position:absolute;background:gray;left:0px;top:0px;width:100%;z-index:5'>");

        // build popup window frame
        var jPopContainer = $("<div class='popContainer' style='position:absolute;left:219px;top:78px;z-index:10;border:solid 2px gray;background:white;width:415px;'>");
        jPopContainer.append("<div class='popClose' style='float:right;margin-top:-20px;margin-right:-20px;width:35px;height:35px;background:url(/images/closeMap.png) no-repeat;cursor:pointer;zoom:1;position:relative;'></div>");

        var jPopContent = $("<div class='popContent'></div>");
        jPopContainer.append(jPopContent);

        // register events;
        jPopContainer.find(".popClose").click(function () {
            Popup.Close();
        })

        divContainer.append(jPopContainer);

        divContainer.append("<div style='clear:both;height:0px'></div>");

        return jPopContent;
    },

    /**********************************************************************************************

    NAME: Close

    DESCRIPTION:
    Close all popup modal windows in this page.

    PARAS: None

    RETURNS: None

    **********************************************************************************************/
    Close: function () {

        if (this.Closing != null) {
            this.Closing($(".popContent"));
        }

        // it's the background of popup windows
        $(".popBackground").remove();

        // popup container's frame, including closing button;
        $(".popContainer").remove();

        // popup contents, user data is put inside.
        $(".popContent").remove();
    },

    // Event of preclose;
    Closing: null
}

/**************************************************************************************************
NAME:   SetCookie

DESCRIPTION:
create and set a cookies

PARAS:  cookie name, cookie value

RETURNS: No return

**************************************************************************************************/
function setCookie(name, value) {
    //this cookies will be saved by __days
    var Days = 1;
    var exp = new Date();
    exp.setTime(exp.getTime() + Days * 24 * 60 * 60 * 1000);
    document.cookie = name + "=" + escape(value) + ";expires=" + exp.toGMTString();
}

/**************************************************************************************************
NAME:   getCookie

DESCRIPTION:
get a cookie value by name

PARAS:  cookie name

RETURNS: cookie value

**************************************************************************************************/
function getCookie(name) {
    var arr = document.cookie.match(new RegExp("(^| )" + name + "=([^;]*)(;|$)"));
    if (arr != null) return unescape(arr[2]); return null;

}

/**************************************************************************************************
NAME:   delCookie

DESCRIPTION:
delete a cookie value by name

PARAS:  cookie name

RETURNS: No Return

**************************************************************************************************/
function delCookie(name) {
    var exp = new Date();
    exp.setTime(exp.getTime() - 1);
    var cval = getCookie(name);
    if (cval != null) document.cookie = name + "=" + cval + ";expires=" + exp.toGMTString();
}


/**************************************************************************************************
NAME:   getHashCode

DESCRIPTION:
get the hascode of a string

PARAS:  string value

RETURNS: the hascode of the string

**************************************************************************************************/
function hashCode(str) {
    var h = 0, off = 0;
    var len = str.length;
    for (var i = 0; i < len; i++) {
        h = 31 * h + str.charCodeAt(off++);
    }

    return h;
}

/**************************************************************************************************
NAME:   MicroBlogComment Object

DESCRIPTION:


PARAS:  No Paras

RETURNS: No return

**************************************************************************************************/
function MicroBlogComment(Index, Content, LocationId, LocationName, PublishedTime, Source,
           ThumbnailPic, UserId, UserScreenName, UserProfileImageUrl, Opinion) {
    //current index
    this.Index               = Index;
    this.Content             = Content;
    this.LocationId          = LocationId;
    this.LocationName        = LocationName;
    this.PublishedTime       = PublishedTime;
    this.Source              = Source;
    this.ThumbnailPic        = ThumbnailPic;
    this.UserId              = UserId;
    this.UserScreenName      = UserScreenName;
    this.UserProfileImageUrl = UserProfileImageUrl;
    this.Opinion             = Opinion;
}

/**************************************************************************************************
NAME:   QueryString

DESCRIPTION:
get the current city or attraction id by the url params 

PARAS:  No Paras

RETURNS: No return

**************************************************************************************************/
function QueryString() {

    var name, value, i;
    var str = location.href; //get url
    var num = str.indexOf("?")
    str = str.substr(num + 1); //get the para after ?
    var arrtmp = str.split("&"); //make them to array
    for (i = 0; i < arrtmp.length; i++) {
        num = arrtmp[i].indexOf("=");
        if (num > 0) {
            name = arrtmp[i].substring(0, num); //get param name
            value = arrtmp[i].substr(num + 1); //get param value
            this[name] = value; //define object  
        }
    }
}

/**************************************************************************************************
NAME:   IsAttraction

DESCRIPTION:
wether current is a attraction

PARAS:  locationId

RETURNS:is attraciton return true else resturn false

**************************************************************************************************/
function IsAttraction(locationId) {

    if (locationId >= 60000000) {
        return true;
    }
    else {
        return false;
    }
}

/*************************************
NAME:   GetMicroBlogSource

DESCRIPTION:
get the Chinese name from the English Microblog Source

PARAS:  members

RETURNS: No return

**************************************/
function GetMicroBlogSource(sourceName) {
    switch (sourceName) {
        case "tencent":
            return "腾讯微博";
        case "sina":
            return "新浪微博";
        case "netease":
            return "网易微博";
        case "sohu":
            return "搜狐微博";
        default:
            return "未知";
    };
}


function GetBlogLink(sourceName) {
    var blogLink = "";
    if (sourceName == "tencent") {
        blogLink = "http://t.qq.com/";
    }
    if (sourceName == "sina") {
        blogLink = "http://www.weibo.com/";
    }
    if (sourceName == "netease") {
        blogLink = "http://t.163.com/";
    }
    if (sourceName == "sohu") {
        blogLink = "http://t.sohu.com";
    }

    return blogLink;
}

function UserProfileLoadError(obj) {
    obj.setAttribute("src", "\\images\\Microblog\\NoPhoto.png");
}


function ImageLoadFaild(obj) {
    obj.setAttribute("src", "\\images\\packageDefault.jpg");
}

/*************************************
NAME:   ConvertJSONDateToJSDateObject

DESCRIPTION:
conver a jason formate datetime to a javascript datetime object

PARAS:  jason string 

RETURNS:the js datetime object 

**************************************/
function ConvertJSONDateToJSDateObject(JSONDateString) {
    var date = new Date(parseInt(JSONDateString.replace("/Date(", "").replace(")/", ""), 10));
    return date;
}


/**************************************************************************************************
NAME:   stringToDateTime

DESCRIPTION:
convert a datetime to the time lag(like three minutes ago)

PARAS:  the datetime to be converted

RETURNS:the convert result

**************************************************************************************************/
function stringToDateTime(postdate) {

    var second = 1000;
    var minutes = second * 60;
    var hours = minutes * 60;
    var days = hours * 24;
    var months = days * 30;
    var twomonths = days * 365;
    var myDate = new Date(Date.parse(postdate));
    if (isNaN(myDate)) {
        myDate = new Date(postdate.replace(/-/g, "/"));
    }
    var nowtime = new Date();
    var longtime = nowtime.getTime() - myDate.getTime();
    var showtime = 0;
    if (longtime > months * 2) {
        return postdate;
    }
    else if (longtime > months) {
        return "1个月前";
    }
    else if (longtime > days * 7) {
        return ("1周前");
    }
    else if (longtime > days) {
        return (Math.floor(longtime / days) + "天前");
    }
    else if (longtime > hours) {
        return (Math.floor(longtime / hours) + "小时前");
    }
    else if (longtime > minutes) {
        return (Math.floor(longtime / minutes) + "分钟前");
    }
    else if (longtime > second) {
        return (Math.floor(longtime / second) + "秒前");
    } else {
        return ("1秒前");
    }
}






