﻿/*
Description:
    The file provides general helper functions.
Change Log:
    Created by Vladimir Trufanov, Feb 22, 2008.
*/

var RecSep = "\t";   //record separator
var FieldSep = "\f"; //field separator
var Yes = "1";       //"true" value for hidden fields
var AddOnTheFlyItemID = -1; //ID of the "Add..." dropdown list item
var EncodedDateFormat = "yyyyMMdd";

function val(controlID){return document.getElementById(controlID).value.trim();}
function setVal(controlID, val){document.getElementById(controlID).value = val;}
function isChecked(controlID){return document.getElementById(controlID).checked;}
function setChecked(controlID, checked){document.getElementById(controlID).checked = ifUndef(checked, true);}
function getHtml(controlID){return document.getElementById(controlID).innerHTML;}
function setHtml(controlID, v){document.getElementById(controlID).innerHTML = v;}
function setReadOnly(controlID, readOnly){document.getElementById(controlID).readOnly = readOnly ? "readonly" : "";}
function click(controlID, delay){
    var f = function(){document.getElementById(controlID).click();};
    if(ifUndef(delay, -1) == -1) f(); else window.setTimeout(f, delay);
}
function dblclick(controlID){click(controlID); click(controlID);}
function enable(controlID, enable){document.getElementById(controlID).disabled = !ifUndef(enable, true);}
function focus(controlOrItsID, delay){
    var f = function(){getNode(controlOrItsID).focus();}
    if(ifUndef(delay, -1) == -1) f(); else window.setTimeout(f, delay);
}
function select(controlID){document.getElementById(controlID).select();}
function display(controlID, bDisplay){
    displayCtrl(document.getElementById(controlID), bDisplay);
}
function displayCtrl(control, bDisplay){
    control.style.display = (bDisplay? "": "none");
}
function isDisplayedCtrl(control){return control.style.display != "none";}
function setVisibility(controlOrItsID, visible) {
    var c = getNode(controlOrItsID);
    c.style.visibility = (visible? "": "hidden");
}
function navigateTo(url){ var f = document.forms[0]; f.action = url; f.submit();}
function moveNodeToBody(nodeOrItsID){
    var n = getNode(nodeOrItsID);
    document.body.insertBefore(n.parentNode.removeChild(n), null);
}
function moveNodeToHtmlForm(nodeOrItsID){
    var n = getNode(nodeOrItsID);
    document.forms[0].insertBefore(n.parentNode.removeChild(n), null);
}

function isDefined(v){return typeof(v) != "undefined";}
function ifUndef(v1, v2){return isDefined(v1)? v1: v2;}
function toBit(boolVal){return boolVal? 1: 0;}
function toInt(numStr){return parseInt(numStr, 10);}
function nullIfZero(num){return num == 0 ? null : num;}

function addOnLoad(handler){Sys.Application.add_load(handler);}
function removeOnLoad(handler){Sys.Application.remove_load(handler);}

function addOnFirstLoad(handler){
    addOnLoad(_onload);
    function _onload(){removeOnLoad(_onload); handler();}
}

function initPostback(postbackButtonID, onLoadHandler){
    var bWaitForm = (typeof(showWaitIndicator) == 'undefined');
    
    addOnFirstLoad(_onload);
    if (bWaitForm) showWaitForm(); else showWaitIndicator();
    click(postbackButtonID,2);
    
    function _onload(){
        if (bWaitForm) hideWaitForm(); else hideWaitIndicator();
        if(onLoadHandler) onLoadHandler();
    }
}

function checkError(hiddenID){
    var e = val(hiddenID);
    
    if(e != '') {alert(e); setVal(hiddenID, '');}
    return e == '';
}

// limit the text in text area during input
function limitTextArea(field, maxlimit) {
    if (field.value.length > maxlimit) 
        field.value = field.value.substring(0, maxlimit);
}
function limitObjectiveTextArea(field) { limitTextArea(field, 300); } //DM, 2010-03-02, TRAC#59  Increase Description character max limit to 300  
function limitRiskTextArea(field) { limitTextArea(field, 150); }
// M1361
function limitNoteTextArea(field) { limitTextArea(field, 500); }

function getTextAreaContent(textareaID) {
    return isIE() ? getHtml(textareaID).toText().trim() : // val() does not return '\n' in IE
                    val(textareaID);
}

String.prototype.toText = function(){
    return this.replace(/&nbsp;/gi, ' ').replace(/<br>/gi, '\n').replace(/&amp;/gi, '&').
                replace(/&lt;/gi, '<').replace(/&gt;/gi, '>'); //&nbsp; conversion is for M1098
}
String.prototype.toHtml = function(){
    return this.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').
                replace(/(\r\n)|(\n)/g, '<br>');
}
String.prototype.cut = function(charAmount){
    if(!charAmount) charAmount = 1;
    return this.length <= charAmount? "": this.substr(0, this.length - charAmount);
}

Array.prototype.first = function() { return this[0]; }

// returns array of records taken from the specified hidden control,
// or null if no records are available.
function getRecords(hidControlID, bClear)
{
    var f = $get(hidControlID).value; //val(hidControlID) trims FieldSep if it's the first/last character

    if(ifUndef(bClear, true)) setVal(hidControlID, ""); //to decrease traffic data on postback
    return (f != ""? f.split(RecSep): null);
}

function getRecordFields(record)
{
    return (record != "" ? record.split(FieldSep): null);
}

//indexByID is optional (false by default).
//bClearHidden is optional (default defined by getRecords()).
function parseRecords(hidControlID, templateObject, indexByID, bClearHidden){
    var records = getRecords(hidControlID, bClearHidden);
    if(!records) return null;
    
    var typeInt = 0, typeBool = 1, typeStr = 2;
    var fieldTypes = getFieldTypes();
    var result = [];
    for(var i = 0, n = records.length; i < n; i++){
        var fields = getRecordFields(records[i]);
        var fieldIndex = 0;
        var recObj = {};
        
        for(var property in templateObject)
            recObj[property] = getTypedField(fields[fieldIndex++], property);
        if(indexByID) result[fields[0]/*id*/] = recObj;
            result.push(recObj);
    }
    return result;
    
    function getFieldTypes() {
        var types = {};
        for(var property in templateObject)
            switch (typeof(templateObject[property])) {
                case 'number': types[property] = typeInt; break;
                case 'boolean': types[property] = typeBool; break;
                default: types[property] = typeStr;
            }
        return types;
    }
    function getTypedField(strVal, property) {
        switch (fieldTypes[property]) {
            case typeInt: return toInt(strVal);
            case typeBool: return strVal == 'true';
            default /*typeStr*/: return strVal;
        }
    }
}

// returns array of fields taken from the specified hidden control,
// or null if no fields are available.
function getFields(hidControlID)
{
    var f = val(hidControlID);
    return (f != ""? f.split(FieldSep): null);
}

function joinFields(){
    var r = arguments[0];
    for(var i = 1, n = arguments.length; i < n; i++) r += FieldSep + arguments[i];
    return r;
}

function isBlank(s){return s.replace(/^ +| +$/g, "") == "";}

function validateForBlank(controlID, controlCaption){
    if(!isBlank(val(controlID))) return true;
    msg(controlCaption + " must be specified.", controlID);
    return false;
}

function validateEmailField(controlID, controlCaption) {
    var str=val(controlID);
    var filter = /^([a-zA-Z0-9_.-])+@(([a-zA-Z0-9-])+.)+([a-zA-Z0-9]{2,4})+$/;

    if (filter.test(str)) return true;
    msg(controlCaption + " in not valid email.", controlID);
    return false;
}

// the time out is used to ensure control focusing for the case when
// the focus was taken by the default button of an asp:Panel control.        
function msg(message, controlOrControlID)
{
    alert(message);
    window.setTimeout(function(){getNode(controlOrControlID).focus();}, 10);
}

function getNode(nodeOrNodeID){
    return typeof(nodeOrNodeID) == "string"? $get(nodeOrNodeID): nodeOrNodeID;
}

function setProgressCaption(button)
{
    if(button.value == "OK") button.value = "Processing...";
    else {
        var c = button.value, n = c.length;
        button.value = (c.substr(n - 1) == "e"? c.substring(0, n - 1): c) + "ing...";
    }
}
// cancelButtonID & actionCaption are optional.
function setupButtonsForSubmit(okButtonID, cancelButtonID, actionCaption)
{
    // time out is used because otherwise postback does not occur:
    window.setTimeout(
    function(){
        var btn = $get(okButtonID);
        
        btn.originalCaption = btn.value;

        if(actionCaption) btn.value = actionCaption;
        else setProgressCaption(btn);
        
        btn.disabled = true;
        if(cancelButtonID && cancelButtonID != ""){
            var cancel = $get(cancelButtonID);
            cancel.className = 'cancel-button-disabled';
            cancel.disabled = true;
        }
    }, 0);
}
// cancelButtonID & okCaption are optional.
function restoreButtons(okButtonID, cancelButtonID, okCaption)
{
    var btnOK = $get(okButtonID);
    
    if(btnOK.originalCaption) okCaption = btnOK.originalCaption;
    else if(!okCaption) okCaption = "OK";
    btnOK.value = okCaption;
    btnOK.disabled = false;
    if(cancelButtonID && cancelButtonID != ""){
        var cancel = $get(cancelButtonID);
        cancel.className = 'cancel-button';
        cancel.disabled = false;
    }
}
// successMessage is optional.
function onResponse(okButtonID, cancelButtonID, errorMessage, successMessage)
{
    restoreButtons(okButtonID, cancelButtonID);
    if(errorMessage != "")
        if(cancelButtonID != "") msg(errorMessage, cancelButtonID);
        else alert(errorMessage);
    else
    {
        if(successMessage) alert(successMessage);
        if(cancelButtonID != "") window.setTimeout(function(){$get(cancelButtonID).click();}, 0);
    }
}

// Cancels bubbling of Enter pressing event.
// If buttonID is specified, simulates click on the button with buttonID id.
// The code is adapted from ASP.NET's WebForm_FireDefaultButton().
function cancelEnterBubble(eventObj, buttonID)
{
    if(eventObj.keyCode == Sys.UI.Key.enter)
    {
        eventObj.cancelBubble = true;
        if(eventObj.stopPropagation) eventObj.stopPropagation();
        if(buttonID) click(buttonID);
        return false;
    }
    return true;
}

function getComboValue(comboBox){
    if(comboBox.selectedIndex == -1) return null;
    return comboBox.options[comboBox.selectedIndex].value;
}

// Set the combo box selected item by value
function setComboValue(comboBox, value){
    for(var i = 0; i < comboBox.length; i++){
        if (comboBox.options[i].value == value){
            comboBox.selectedIndex = i;
            return;
        }
    }
    comboBox.selectedIndex = -1;
}

function getListText(list, index) {
    if (!isDefined(index)) index = list.selectedIndex;
    return index == -1 ? '' : list.options[index].text;
}
function setListText(list, text) {
    for(var i = 0; i < list.length; i++){
        if (list.options[i].text == text){
            list.selectedIndex = i;
            return;
        }
    }
    list.selectedIndex = -1;
}
function fillList(ctrList, items){
    var selIndex = ctrList.selectedIndex;
    
    ctrList.options.length = 0;
    for(var i = 0, n = items.length; i < n; i++)
        addOption(ctrList, items[i].text, items[i].value);
    if(selIndex != -1) ctrList.selectedIndex = selIndex;
}

function getInputElements(parentNode, elementType){
    var e = parentNode.getElementsByTagName("input");
    var r = new Array();
    
    for(var i = 0, n = e.length; i < n; i++)
        if(e[i].type == elementType) r.push(e[i]);
    return r;
}
function getHiddenElements(parentNode){return getInputElements(parentNode, "hidden");}
function getCheckboxes(parentNode) { return getInputElements(parentNode, "checkbox"); }

function addOption(ctrSelect, optionText, optionValue, bSelected)
{
    var option = document.createElement("OPTION");
    
    option.text = optionText;
    option.value = optionValue;
    ctrSelect.options.add(option);
    //option.selected = true; does not work in IE6 if ctrSelect is hidden via display:none:
    if(bSelected) setComboValue(ctrSelect, optionValue);
}

function removeOption(ctrSelect, intIndex, bSetupSelection)
{
    var selectedIndex = ctrSelect.selectedIndex;
    
    ctrSelect.remove(intIndex);
    if (bSetupSelection && intIndex == selectedIndex) {
        var count = ctrSelect.options.length;
        if (count > 0) {
            if (selectedIndex == count) selectedIndex--;
            ctrSelect.selectedIndex = selectedIndex;
        }
    }
}

function getOptionIndex(ctrSelect, itemValue) {
    var options = ctrSelect.options;
    for (var i = 0, n = options.length; i < n; i++)
        if (options[i].value == itemValue) return i;
    return -1;
}

function clearOptions(ctrSelect)
{
	ctrSelect.options.length = 0;
}

function amendCaption(controlID, caption){
    var c = document.getElementById(controlID);
    c.originalCaption = c.value;
    c.value = caption;
}

function restoreCaption(controlID){
    var c = document.getElementById(controlID);
    c.value = c.originalCaption;
}

Date.prototype.nextQuarterStart = function(){
    var month = this.getMonth();
    var quarterStart = new Date(this.getFullYear(), month - month % 3, 1);
    return quarterStart.addMonths(3);
}
Date.prototype.nextMonthStart = function(){
    var monthStart = new Date(this.getFullYear(), this.getMonth(), 1);
    return monthStart.addMonths(1);
}
Date.prototype.addMonths = function(intMonths){
    this.setMonth(this.getMonth() + intMonths);
    return this;
}
Date.prototype.addDays = function(intDays){
    this.setDate(this.getDate() + intDays);
    return this;
}
Date.prototype.daysDiff = function(date){
    var minuteDiff = (this - date) / (1000 * 60);
    
    //this and date can be in different time zones 
    //due to daylight saving (eg 26 and 25 Oct 2009):
    minuteDiff -= this.getTimezoneOffset() - date.getTimezoneOffset();
    
    return Math.ceil(minuteDiff / (60 * 24));
}
function decodeDateToString(encodedDate){
    if(encodedDate == "") return "&lt;none&gt;";
    return decodeDate(encodedDate).format("MM/dd/yyyy");
}
function decodeDate(encodedDate) {
    return Date.parseInvariant(encodedDate, EncodedDateFormat);
}
function encodeDate(date) { return date.format(EncodedDateFormat); }
function getTextboxDate(dateTextboxID) {
    try {
        var p = val(dateTextboxID).split('/');
        // 10 is specified for correct parsing of 08 and 09
        // that otherwise are considered as zero octal numbers:
        var month = parseInt(p[0], 10) - 1;
        var day = parseInt(p[1], 10);
        var year = parseInt(p[2], 10);
        var d = new Date(year, month, day, 0, 0, 0, 0);
        
        if(month != d.getMonth() ||
           day != d.getDate() ||
           year != d.getFullYear()) return null;

        return d;
    }
    catch(e) {return null;}
}

function getOffsetTopOnPage(element){
    var t = 0;
    while(element){
        t += element.offsetTop;
        element = element.offsetParent;
    }
    return t;
}

function saveChart(chartID)
{
    //navigateTo() (rather than window.open) causes an error for the Resources
    //report on click on a resource item after execution of this function.
    //M1066: '/' is used to make URL absolute:
	window.open('/ChartImage.ashx?ImagePath=' + encodeChartImageUrl(chartID));
}
function printChart(chartID, chartCaption)
{
    //M1066: '/' is used to make URL absolute:
    window.open('/ChartImagePrint.aspx?ImagePath=' + encodeChartImageUrl(chartID) +
	            '&ChartCaption=' + encodeURIComponent(chartCaption));
}
function encodeChartImageUrl(chartID){
    var imageUrl = $get(chartID).getElementsByTagName("img")[0].src;
    return encodeURIComponent(imageUrl);
}
function isServiceError(result) { return result.get_message || result.ErrorCode != 0; }
function showServiceError(result) {
    if (typeof(hideWaitForm) != 'undefined') hideWaitForm();
    if (result.get_message) //unhandled error in the web service method?
        alert(result.get_message());
    else {
        var errorMsg = result.ErrorDescription + "\n" + result.ErrorAdditionalDescr;
        alert(errorMsg);
    }
}

function setupBordersAndShadow(popout){
    var tl = getChildByCssClass(popout, 'tl');
    var bl = getChildByCssClass(popout, 'bl');
    var cornerSize = tl.clientHeight;
    var width = popout.clientWidth;
    
    /*widths of left corners are set here to fix:
     (1) IE6 extends left images beyond the form if it has no width specified in css;
     (2) right corners have dots in FF and Safari because left images have the width of the form.*/
    tl.style.width = 
    bl.style.width = (width - cornerSize) + 'px';
    
    //Width and height via css do not work for IE6.
    //M886: width is set for IE6 only because setting it here does not
    //display it for the IE Settings form on the Projects page in IE7.
    //Height is set here for all browsers because setting it via css does not work
    //for the Gantt chart popup in IE7 on 1st popup displaying.
    if(isIE6()) getChildByCssClass(popout, 'bottom-shadow').style.width = width + 'px';
    getChildByCssClass(popout, 'right-shadow').style.height = popout.clientHeight + 'px';
}

function getChildByCssClass(parent, cssClass){
    var children = getChildrenByCssClass(parent, cssClass);
    return children.length == 0? null: children[0];
}
function getChildrenByCssClass(parent, cssClass){
    var nodes = parent.childNodes;
    var children = [];
    
    for(var i = 0, n = nodes.length; i < n; i++)
        if(hasCssClass(nodes[i], cssClass)) children.push(nodes[i]);
    return children;
}
function getAllChildrenByCssClass(parent, cssClass, recursive) {
    var nodes = parent.childNodes;
    var children = [];

    for (var i = 0, n = nodes.length; i < n; i++) {
        if (hasCssClass(nodes[i], cssClass)) {
            children.push(nodes[i]);
        }
        else {
            if (recursive) {
                if (nodes[i].hasChildNodes()) {
                    var items = getAllChildrenByCssClass(nodes[i], cssClass, recursive);
                    for (var j = 0, m = items.length; j < m; j++) children.push(items[j]);
                }
            }
        }
    }
    return children;
}
function hasCssClass(node, cssClass) {
    var re = new RegExp('^(.* )?' + cssClass + '( .*)?$');
    return re.test(node.className);
}
function addCssClass(node, className) {
    if (!hasCssClass(node, className))
        node.className += ' ' + className;
}
function replaceCssClass(node, oldClass, newClass){
    var classes = node.className.split(' ');
    
    for(var i = 0, n = classes.length; i < n; i++)
        if(classes[i] == oldClass){
            classes[i] = newClass;
            node.className = classes.join(' ');
            return;
        }
}
function removeCssClass(node, className) {
    var classes = ' ' + node.className + ' ';

    node.className = classes.replace(new RegExp(' ' + className + ' '), ' ').trim();
}

//fixes incorrect scrollbars drawing for IE 6/7 when the control has overflow-x:auto.
function setupHorizontalScrollbar(controlOrItsID){
    if(isIE()){
        var c = getNode(controlOrItsID);
        //to avoid the vertical (IE7) or horizontal (IE6) scroll bar:
        if(c.offsetHeight != c.clientHeight) c.style.overflowX = 'scroll';
    }
}

function extendArrayMethods(array, itemCaption) {
    array.itemCaption = itemCaption;
    array.recIndex = getRecordIndex;
    array.rec = function(id){ return this[this.recIndex(id)]; };
    array.each = forEach;
    array.insertAtTop = insertAtTop;
    array.remove = function(index){ this.splice(index, 1); };
    array.removeByID = function(id){ this.remove(this.recIndex(id)); };
    array.first = function(){ return this[0]; };
    return array;
    
    function getRecordIndex(id, nullIfNotFound) {
        for (var i = 0, n = this.length; i < n; i++)
            if (this[i].id == id) return i;
        if (nullIfNotFound) return null;
        alert(this.itemCaption + ' with id=' + id + ' is not found.');
    }
    function forEach(func) {
        for (var i = 0, n = this.length; i < n; i++) func(this[i]);
    }
    function insertAtTop(item) {
        if (this.length == 0) this.push(item);
            else this.splice(0, 1, item, this[0]);
    }
}

function onUpdateControlState(stateGroup, controlID, property, value)
{
	PPM.PPMService.UpdateControlState(stateGroup, controlID, property, value,
		onUpdateControlState_Complete, onUpdateControlState_Failed, null);
}
function onUpdateControlState_Complete(result)
{
    if(result.ErrorCode != 0){
        showServiceError(result);
    }
}
function onUpdateControlState_Failed(result)
{
	showServiceError(result);
}

function iif(select, trueOption, falseOption) {
if (select)
    return trueOption
else 
    return falseOption;
}
function isIE(){ return /MSIE \d/.test(navigator.userAgent); }
function isIE6(){ return /MSIE 6\.0/.test(navigator.userAgent); }

if (typeof (Sys) != 'undefined') Sys.Application.notifyScriptLoaded();