'use strict';

var $ = require('jquery');
var ko = require('knockout');
require('knockout.validation');

var makeExtender = function(interceptor) {
    return function(target, options) {
        var result = ko.computed({
            read: target,
            write: function(value) {
                var current = target();
                var toWrite = interceptor(value, options);
                if (current !== toWrite) {
                    target(toWrite);
                } else {
                    if (current !== value) {
                        target.notifySubscribers(toWrite);
                    }
                }
            }
        }).extend({
            notify: 'always'
        });
        result(target());
        return result;
    };
};

var addExtender = function(label, interceptor) {
    ko.extenders[label] = makeExtender(interceptor);
};

var makeRegexValidator = function(regex, message, match) {
    match = match || match === undefined;
    return {
        validator: function(value) {
            if (ko.validation.utils.isEmptyVal(value)) {
                return true;
            }
            return match === regex.test(ko.utils.unwrapObservable(value));
        },
        message: message
    };
};

var cleanURL = function(value) {
    value = $.trim(value);
    if (!value || value.search(/^https?:\/\//i) === 0) {
        return value;
    }
    return 'http://' + value;

    };


addExtender('cleanup', function(value, cleaner) {
    return !!value ? cleaner(value) : '';
});

addExtender('ensureHttp', function(value) {
    if (!value || value.search(/^https?:\/\//i) === 0) {
        return value;
    }
    return 'http://' + value;
});

addExtender('trimmed', function(value) {
    return $.trim(value);
});

addExtender('trimmedURL', function(value) {
    return cleanURL(value);

});

addExtender('trimmedURLArray', function(values) {
    if (values) {
        for (var i = 0; i < values.length; i++) {   
            values[i] = cleanURL(values[i]);
        }
        return values;
    } else {
        return values;
    }
});

var sanitize = function(value) {
    return value.replace(/<\/?[^>]+>/g, '');
};

var sanitizedObservable = function(value) {
    return ko.observable(value).extend({
        cleanup: sanitize
    });
};

/* maps js data one deep to observables
    options:
        exclude: adds listed parameters without making observable
*/
var mapJStoKO = function(data, options) {
    var settings = $.extend({
        exclude: []   //List of object parameters to exclude
    }, options || {});
    var dataOut = {};

    for (var key in data) {
        // checks for key, if in the exclude list
        if (data.hasOwnProperty(key) && $.inArray(key, settings.exclude) === -1) {
            if(Array.isArray(data[key])) {
                dataOut[key] = ko.observableArray(data[key]);
            } else {
                dataOut[key] = ko.observable(data[key]);
            }
        } else if (data.hasOwnProperty(key)) {
            dataOut[key] = data[key]; //excluded parameters
        }
    }
    return dataOut;
};

// Add custom validators

ko.validation.rules.minDate = {
    validator: function (val, minDate) {
        // Skip if values empty
        var uwVal = ko.utils.unwrapObservable(val);
        var uwMin = ko.utils.unwrapObservable(minDate);
        if (uwVal === null || uwMin === null) {
            return true;
        }
        // Skip if dates invalid
        var dateVal = new Date(uwVal);
        var dateMin = new Date(uwMin);
        if (dateVal.toString() === 'Invalid Date' || dateMin.toString() === 'Invalid Date') {
            return true;
        }
        // Check if end date is ongoing
        if (uwVal === 'ongoing') {
            return true;
        }
        // Compare dates
        return dateVal >= dateMin;
    },
    message: 'End date must be greater than or equal to the start date.'
};

ko.validation.rules.pyDate = {
    validator: function (val) {
        // Skip if values empty
        var uwVal = ko.utils.unwrapObservable(val);
        if (uwVal === null) {
            return true;
        }
        // Skip if dates invalid
        var dateVal = new Date(uwVal);
        if (dateVal.toString() === 'Invalid Date') {
            return true;
        }
        // Compare years
        return parseInt(uwVal) >= 1900;
    },
    message: 'Date must be greater than or equal to 1900.'
};

ko.validation.rules.notInFuture = {
    validator: function (val) {
        // Skip if values empty
        var uwVal = ko.utils.unwrapObservable(val);
        if (uwVal === null || uwVal === undefined) {
            return true;
        }

        //Skip if dates invalid
        var dateVal = new Date(uwVal);

        if (dateVal.toString() === 'Invalid Date') {
            return true;
        }
        // Compare dates
        var now = new Date();
        return dateVal <= now;

    },
    message: 'Please enter a date prior to the current date.'
};


ko.validation.rules.year = makeRegexValidator(
    new RegExp('^\\d{4}$'),
    'Please enter a valid year.'
);


ko.validation.rules.url = makeRegexValidator(
    new RegExp(
        '^(?:http|ftp)s?://' +
            '(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\\.)+(?:[A-Z]{2,6}\\.?|[A-Z0-9-]{2,}\\.?)|' +
            'localhost|' +
            '\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|' +
            '\\[?[A-F0-9]*:[A-F0-9:]+\\]?)' +
            '(?::\\d+)?' +
            '(?:/?|[/?]\\S+)$',
        'i'
    ),
    'Please enter a valid URL.'
);

ko.validation.rules.mustEqual = {
    validator: function (val, otherVal) {
        return val === otherVal;
    },
    message: 'The field does not match the required input.'
};


// Add custom effects

// fadeVisible : http://knockoutjs.com/examples/animatedTransitions.html
ko.bindingHandlers.fadeVisible = {
    init: function(element, valueAccessor) {
        // Initially set the element to be instantly visible/hidden depending on the value
        var value = valueAccessor();
        $(element).toggle(ko.unwrap(value)); // Use "unwrapObservable" so we can handle values that may or may not be observable
    },
    update: function(element, valueAccessor) {
        // Whenever the value subsequently changes, slowly fade the element in or out
        var value = valueAccessor();
        ko.unwrap(value) ? $(element).fadeIn() : $(element).hide().fadeOut();
    }
};


// Expose public utilities

module.exports = {
    makeExtender: makeExtender,
    addExtender: addExtender,
    makeRegexValidator: makeRegexValidator,
    sanitizedObservable: sanitizedObservable,
    mapJStoKO: mapJStoKO
};

@@ -1424,17 +1424,16 @@
     %7D;%0A%0A
-%0A
 addExten
@@ -1839,254 +1839,8 @@
 );%0A%0A
-addExtender('trimmedURLArray', function(values) %7B%0A    if (values) %7B%0A        for (var i = 0; i %3C values.length; i++) %7B   %0A            values%5Bi%5D = cleanURL(values%5Bi%5D);%0A        %7D%0A        return values;%0A    %7D else %7B%0A        return values;%0A    %7D%0A%7D);%0A%0A
 var 

remove unnecessary extender