<commit_before>/*
 By Osvaldas Valutis, www.osvaldas.info
 Available for use under the MIT License
 */
;(function ($, window, document, undefined) {
    'use strict';
    // COMPONENTS //
        var $activityObject = $('<div/>')
        .attr('id','imagelightbox-loading')
        .append($('<div/>')),
        $arrowLeftObject = $('<button/>',{
            type: 'button',
            class: 'imagelightbox-arrow imagelightbox-arrow-left'}),
        $arrowRightObject = $('<button/>',{
            type: 'button',
            class: 'imagelightbox-arrow imagelightbox-arrow-right'}),
        $arrows = $arrowLeftObject.add($arrowRightObject),
        $captionObject = $('<div/>', {
            id: 'imagelightbox-caption'
        }),
        $buttonObject =  $('<a/>', {
            id: 'imagelightbox-close'
        }),
        $overlayObject = $('<div/>', {
            id:'imagelightbox-overlay'
        }),
        $navObject = $('<div/>', {
            id: 'imagelightbox-nav'
        }),
        //
        $wrapper = $('<div/>', {
            id: 'imagelightbox-wrapper'
        });

    var cssTransitionSupport = function () {
            var s = document.body || document.documentElement;
            s = s.style;
            if (s.WebkitTransition === '') {
                return '-webkit-';
            }
            if (s.MozTransition === '') {
                return '-moz-';
            }
            if (s.OTransition === '') {
                return '-o-';
            }
            if (s.transition === '') {
                return '';
            }
            return false;
        },

        isCssTransitionSupport = cssTransitionSupport() !== false,

        cssTransitionTranslateX = function (element, positionX, speed) {
            var options = {}, prefix = cssTransitionSupport();
            options[prefix + 'transform'] = 'translateX(' + positionX + ')';
            options[prefix + 'transition'] = prefix + 'transform ' + speed + 's linear';
            element.css(options);
        },

        hasTouch = ( 'ontouchstart' in window ),
        hasPointers = window.navigator.pointerEnabled || window.navigator.msPointerEnabled,
        wasTouched = function (event) {
            if (hasTouch) {
                return true;
            }

            if (!hasPointers || typeof event === 'undefined' || typeof event.pointerType === 'undefined') {
                return false;
            }

            if (typeof event.MSPOINTER_TYPE_MOUSE !== 'undefined') {
                if (event.MSPOINTER_TYPE_MOUSE !== event.pointerType) {
                    return true;
                }
            }
            else if (event.pointerType !== 'mouse') {
                return true;
            }

            return false;
        };

    $.fn.imageLightbox = function (opts) {
        var options = $.extend({
                selector:       'a[data-imagelightbox]',
                id:             'imagelightbox',
                allowedTypes:   'png|jpg|jpeg||gif', // TODO make it work again
                animationSpeed: 250,
                activity:       false,
                arrows:         false,
                button:         false,
                caption:        false,
                enableKeyboard: true,
                lockBody:       false,
                navigation:     false,
                overlay:        false,
                preloadNext:    true,
                quitOnEnd:      false,
                quitOnImgClick: false,
                quitOnDocClick: true,
                quitOnEscKey:   true,
                onStart: function () {
                    if (options.arrows) {
                        arrowsOn(this);
                    }
                    if (options.navigation) {
                        navigationOn(this, options.selector);
                    }
                    if (options.overlay) {
                        overlayOn();
                    }
                    if (options.button) {
                        closeButtonOn();
                    }
                    if (options.lockBody) {
                        lockBody(true);
                    }
                },
                onEnd: function () {
                    $wrapper.remove().find("*").remove();
                    if (options.lockBody) {
                        lockBody(false);
                    }
                },
                onLoadStart: function () {
                    if (options.activity) {
                        activityIndicatorOn();
                    }
                    if (options.caption) {
                        captionOff();
                    }
                },
                onLoadEnd: function () {
                    if (options.activity) {
                        activityIndicatorOff();
                    }
                    if (options.arrows) {
                        $arrows.css('display', 'block');
                    }
                    if (options.navigation) {
                        navigationUpdate(options.selector);
                    }
                    if (options.caption) {
                        captionOn();
                    }
                },
                previousTarget: function () {
                    return this.previousTargetDefault();
                },
                previousTargetDefault: function () {
                    var targetIndex = targets.index(target) - 1;
                    if (targetIndex < 0) {
                        if (options.quitOnEnd === true) {
                            _quitImageLightbox();
                            return false;
                        }
                        else {
                            targetIndex = targets.length - 1;
                        }
                    }
                    target = targets.eq(targetIndex);
                },
                nextTarget: function () {
                    return this.nextTargetDefault();
                },
                nextTargetDefault: function () {
                    var targetIndex = targets.index(target) + 1;
                    if (targetIndex >= targets.length) {
                        if (options.quitOnEnd === true) {
                            _quitImageLightbox();
                            return false;
                        }
                        else {
                            targetIndex = 0;
                        }
                    }
                    target = targets.eq(targetIndex);
                }
            }, opts),
            activityIndicatorOn = function () {
                $wrapper.append($activityObject);
            },
            activityIndicatorOff = function () {
                $('#imagelightbox-loading').remove();
            },
            lockBody = function (toggle) {
                if (toggle) {
                    $("body").css("overflow","hidden");
                } else {
                    $("body").css("overflow","scroll");
                }
            },
            overlayOn = function () {
                $wrapper.append($overlayObject);
            },
            closeButtonOn = function () {
                $buttonObject.appendTo($wrapper).on('click', function () {
                    _quitImageLightbox();
                    return false;
                });
            },
            captionOn = function () {
                var description = $(target).find('img').attr('alt');
                if (description && description.length > 0) {
                    $wrapper.append($captionObject.text(description));
                }
            },
            captionOff = function () {
                $captionObject.remove();
            },
            navigationOn = function (instance, selector) {
                var images = $(selector);
                if (images.length) {
                    for (var i = 0; i < images.length; i++) {
                        $navObject.append($('<a/>',{href:'#'}));
                    }
                    $wrapper.append($navObject);
                    $navObject.on('click touchend', function () {
                        return false;
                    });
                    var navItems = $navObject.find('a');
                    navItems.on('click touchend', function () {
                        var $this = $(this);
                        if (images.eq($this.index()).attr('href') !== $('#imagelightbox').attr('src')) {
                            var tmpTarget = targets.eq($this.index());
                            if (tmpTarget.length) {
                                var currentIndex = targets.index(target);
                                target = tmpTarget;
                                _loadImage($this.index() < currentIndex ? 'left' : 'right');
                            }
                        }
                        navItems.removeClass('active');
                        navItems.eq($this.index()).addClass('active');
                        return false;
                    }).on('touchend', function () {
                        return false;
                    });
                }
            },
            navigationUpdate = function (selector) {
                var items = $navObject.find('a');
                items.removeClass('active');
                items.eq($(selector).filter('[href="' + $('#imagelightbox').attr('src') + '"]').index(selector)).addClass('active');
            },
            navigationOff = function () {
                $('#imagelightbox-nav').remove();
            },
            arrowsOn = function () {
                $wrapper.append($arrows);
                $arrows.on('click touchend', function (e) {
                    e.preventDefault();
                    if ($(this).hasClass('imagelightbox-arrow-left')) {
                        _loadPreviousImage();
                    } else {
                        _loadNextImage();
                    }
                    return false;
                });
            },
            arrowsOff = function () {
                $('.imagelightbox-arrow').remove();
            },

            targets = $([]),
            target = $(),
            image = $(),
            imageWidth = 0,
            imageHeight = 0,
            swipeDiff = 0,
            inProgress = false,

            /* TODO make it work again
             isTargetValid = function (element) {
             var classic = $(element).prop('tagName').toLowerCase() === 'a' && ( new RegExp('.(' + options.allowedTypes + ')$', 'i') ).test($(element).attr('href'));
             var html5 = $(element).attr('data-lightbox') !== undefined;
             return classic || html5;
             },
             */

            _setImage = function () {
                if (!image.length) {
                    return true;
                }

                var screenWidth = $(window).width() * 0.8,
                    wHeight = (window.innerHeight) ? window.innerHeight : $(window).height(),
                    screenHeight = wHeight * 0.9,
                    tmpImage = new Image();

                tmpImage.src = image.attr('src');
                tmpImage.onload = function () {
                    imageWidth = tmpImage.width;
                    imageHeight = tmpImage.height;

                    if (imageWidth > screenWidth || imageHeight > screenHeight) {
                        var ratio = imageWidth / imageHeight > screenWidth / screenHeight ? imageWidth / screenWidth : imageHeight / screenHeight;
                        imageWidth /= ratio;
                        imageHeight /= ratio;
                    }

                    image.css({
                        'width': imageWidth + 'px',
                        'height': imageHeight + 'px',
                        'top': ( wHeight - imageHeight ) / 2 + 'px',
                        'left': ( $(window).width() - imageWidth ) / 2 + 'px'
                    });
                };
            },

            _loadImage = function (direction) {
                if (inProgress) {
                    return false;
                }

                direction = typeof direction === 'undefined' ? false : direction === 'left' ? 1 : -1;

                if (image.length) {
                    var params = {'opacity': 0};
                    if (isCssTransitionSupport) {
                        cssTransitionTranslateX(image, ( 100 * direction ) - swipeDiff + 'px', options.animationSpeed / 1000);
                    }
                    else {
                        params.left = parseInt(image.css('left')) + 100 * direction + 'px';
                    }
                    image.animate(params, options.animationSpeed, function () {
                        _removeImage();
                    });
                    swipeDiff = 0;
                }

                inProgress = true;
                if (options.onLoadStart !== false) {
                    options.onLoadStart();
                }

                setTimeout(function () {
                    var imgPath = target.attr('href');
                    // if ( imgPath === undefined ) {
                    //     imgPath = target.attr( 'data-lightbox' );
                    // }
                    image = $('<img id="' + options.id + '" />')
                        .attr('src', imgPath)
                        .on('load', function () {
                            var params = {'opacity': 1};

                            image.appendTo($wrapper);
                            _setImage();
                            image.css('opacity', 0);
                            if (isCssTransitionSupport) {
                                cssTransitionTranslateX(image, -100 * direction + 'px', 0);
                                setTimeout(function () {
                                    cssTransitionTranslateX(image, 0 + 'px', options.animationSpeed / 1000);
                                }, 50);
                            } else {
                                var imagePosLeft = parseInt(image.css('left'));
                                params.left = imagePosLeft + 'px';
                                image.css('left', imagePosLeft - 100 * direction + 'px');
                            }

                            image.animate(params, options.animationSpeed, function () {
                                inProgress = false;
                                if (options.onLoadEnd !== false) {
                                    options.onLoadEnd();
                                }
                            });
                            if (options.preloadNext) {
                                var nextTarget = targets.eq(targets.index(target) + 1);
                                if (!nextTarget.length) {
                                    nextTarget = targets.eq(0);
                                }
                                $('<img />').attr('src', nextTarget.attr('href'));
                            }
                        })
                        .on('error', function () {
                            if (options.onLoadEnd !== false) {
                                options.onLoadEnd();
                            }
                        });

                    var swipeStart = 0,
                        swipeEnd = 0,
                        imagePosLeft = 0;

                    image.on(hasPointers ? 'pointerup MSPointerUp' : 'click', function (e) {
                        e.preventDefault();
                        if (options.quitOnImgClick) {
                            _quitImageLightbox();
                            return false;
                        }
                        if (wasTouched(e.originalEvent)) {
                            return true;
                        }
                        var posX = ( e.pageX || e.originalEvent.pageX ) - e.target.offsetLeft;
                        if (imageWidth / 2 > posX) {
                            _loadPreviousImage();
                        } else {
                            _loadNextImage();
                        }
                    })
                        .on('touchstart pointerdown MSPointerDown', function (e) {
                            if (!wasTouched(e.originalEvent) || options.quitOnImgClick) {
                                return true;
                            }
                            if (isCssTransitionSupport) {
                                imagePosLeft = parseInt(image.css('left'));
                            }
                            swipeStart = e.originalEvent.pageX || e.originalEvent.touches[0].pageX;
                        })
                        .on('touchmove pointermove MSPointerMove', function (e) {
                            if (!wasTouched(e.originalEvent) || options.quitOnImgClick) {
                                return true;
                            }
                            e.preventDefault();
                            swipeEnd = e.originalEvent.pageX || e.originalEvent.touches[0].pageX;
                            swipeDiff = swipeStart - swipeEnd;
                            if (isCssTransitionSupport) {
                                cssTransitionTranslateX(image, -swipeDiff + 'px', 0);
                            } else {
                                image.css('left', imagePosLeft - swipeDiff + 'px');
                            }
                        })
                        .on('touchend touchcancel pointerup pointercancel MSPointerUp MSPointerCancel', function (e) {
                            if (!wasTouched(e.originalEvent) || options.quitOnImgClick) {
                                return true;
                            }
                            if (Math.abs(swipeDiff) > 50) {
                                if (swipeDiff < 0) {
                                    _loadPreviousImage();
                                } else {
                                    _loadNextImage();
                                }
                            } else {
                                if (isCssTransitionSupport) {
                                    cssTransitionTranslateX(image, 0 + 'px', options.animationSpeed / 1000);
                                } else {
                                    image.animate({'left': imagePosLeft + 'px'}, options.animationSpeed / 2);
                                }
                            }
                        });

                }, options.animationSpeed + 100);
            },

            _loadPreviousImage = function () {
                if (options.previousTarget() !== false) {
                    _loadImage('left');
                }
            },

            _loadNextImage = function () {
                if (options.nextTarget() !== false) {
                    _loadImage('right');
                }
            },

            _removeImage = function () {
                if (!image.length) {
                    return false;
                }
                image.remove();
                image = $();
            },

            _openImageLightbox = function ($target) {
                if (inProgress) {
                    return false;
                }
                inProgress = false;
                if (options.onStart !== false) {
                    options.onStart();
                }
                $('body').append($wrapper);
                target = $target;
                _loadImage();
            },

            _quitImageLightbox = function () {
                if (!image.length) {
                    return false;
                }
                image.animate({'opacity': 0}, options.animationSpeed, function () {
                    _removeImage();
                    inProgress = false;
                    if (options.onEnd !== false) {
                        options.onEnd();
                    }
                });
            },

            _addTargets = function( newTargets ) {
                newTargets.each(function () {
                    targets = targets.add($(this));
                });

                newTargets.on('click', function (e) {
                    e.preventDefault();
                    _openImageLightbox($(this));
                });
            };

        this.startImageLightbox = function () {
            if (this.length > 0) {
                _openImageLightbox($(this[0]));
            }
        };

        $(window).on('resize', _setImage);

        $(document).ready(function() {
            if (options.quitOnDocClick) {
                $(document).on(hasTouch ? 'touchend' : 'click', function (e) {
                    if (image.length && !$(e.target).is(image)) {
                        e.preventDefault();
                        _quitImageLightbox();
                    }
                });
            }

            if (options.enableKeyboard) {
                $(document).on('keyup', function (e) {
                    if (!image.length) {
                        return true;
                    }
                    e.preventDefault();
                    if (e.keyCode === 27 && options.quitOnEscKey === true) {
                        _quitImageLightbox();
                    }
                    if (e.keyCode === 37) {
                        _loadPreviousImage();
                    } else if (e.keyCode === 39) {
                        _loadNextImage();
                    }
                });
            }
        });

        $(document).off('click', this.selector);

        _addTargets($(this));

        this.loadPreviousImage = function () {
            _loadPreviousImage();
        };

        this.loadNextImage = function () {
            _loadNextImage();
        };

        this.quitImageLightbox = function () {
            _quitImageLightbox();
            return this;
        };

        this.addToImageLightbox = function(elements)  {
            _addTargets(elements);
        };

        return this;
    };
})(jQuery, window, document);
<commit_msg>Fix intendation<commit_after>/*
 By Osvaldas Valutis, www.osvaldas.info
 Available for use under the MIT License
 */
;(function ($, window, document, undefined) {
    'use strict';
    // COMPONENTS //
    var $activityObject = $('<div/>')
            .attr('id','imagelightbox-loading')
            .append($('<div/>')),
        $arrowLeftObject = $('<button/>',{
            type: 'button',
            class: 'imagelightbox-arrow imagelightbox-arrow-left'}),
        $arrowRightObject = $('<button/>',{
            type: 'button',
            class: 'imagelightbox-arrow imagelightbox-arrow-right'}),
        $arrows = $arrowLeftObject.add($arrowRightObject),
        $captionObject = $('<div/>', {
            id: 'imagelightbox-caption'
        }),
        $buttonObject =  $('<a/>', {
            id: 'imagelightbox-close'
        }),
        $overlayObject = $('<div/>', {
            id:'imagelightbox-overlay'
        }),
        $navObject = $('<div/>', {
            id: 'imagelightbox-nav'
        }),
        //
        $wrapper = $('<div/>', {
            id: 'imagelightbox-wrapper'
        });

    var cssTransitionSupport = function () {
            var s = document.body || document.documentElement;
            s = s.style;
            if (s.WebkitTransition === '') {
                return '-webkit-';
            }
            if (s.MozTransition === '') {
                return '-moz-';
            }
            if (s.OTransition === '') {
                return '-o-';
            }
            if (s.transition === '') {
                return '';
            }
            return false;
        },

        isCssTransitionSupport = cssTransitionSupport() !== false,

        cssTransitionTranslateX = function (element, positionX, speed) {
            var options = {}, prefix = cssTransitionSupport();
            options[prefix + 'transform'] = 'translateX(' + positionX + ')';
            options[prefix + 'transition'] = prefix + 'transform ' + speed + 's linear';
            element.css(options);
        },

        hasTouch = ( 'ontouchstart' in window ),
        hasPointers = window.navigator.pointerEnabled || window.navigator.msPointerEnabled,
        wasTouched = function (event) {
            if (hasTouch) {
                return true;
            }

            if (!hasPointers || typeof event === 'undefined' || typeof event.pointerType === 'undefined') {
                return false;
            }

            if (typeof event.MSPOINTER_TYPE_MOUSE !== 'undefined') {
                if (event.MSPOINTER_TYPE_MOUSE !== event.pointerType) {
                    return true;
                }
            }
            else if (event.pointerType !== 'mouse') {
                return true;
            }

            return false;
        };

    $.fn.imageLightbox = function (opts) {
        var options = $.extend({
                selector:       'a[data-imagelightbox]',
                id:             'imagelightbox',
                allowedTypes:   'png|jpg|jpeg||gif', // TODO make it work again
                animationSpeed: 250,
                activity:       false,
                arrows:         false,
                button:         false,
                caption:        false,
                enableKeyboard: true,
                lockBody:       false,
                navigation:     false,
                overlay:        false,
                preloadNext:    true,
                quitOnEnd:      false,
                quitOnImgClick: false,
                quitOnDocClick: true,
                quitOnEscKey:   true,
                onStart: function () {
                    if (options.arrows) {
                        arrowsOn(this);
                    }
                    if (options.navigation) {
                        navigationOn(this, options.selector);
                    }
                    if (options.overlay) {
                        overlayOn();
                    }
                    if (options.button) {
                        closeButtonOn();
                    }
                    if (options.lockBody) {
                        lockBody(true);
                    }
                },
                onEnd: function () {
                    $wrapper.remove().find("*").remove();
                    if (options.lockBody) {
                        lockBody(false);
                    }
                },
                onLoadStart: function () {
                    if (options.activity) {
                        activityIndicatorOn();
                    }
                    if (options.caption) {
                        captionOff();
                    }
                },
                onLoadEnd: function () {
                    if (options.activity) {
                        activityIndicatorOff();
                    }
                    if (options.arrows) {
                        $arrows.css('display', 'block');
                    }
                    if (options.navigation) {
                        navigationUpdate(options.selector);
                    }
                    if (options.caption) {
                        captionOn();
                    }
                },
                previousTarget: function () {
                    return this.previousTargetDefault();
                },
                previousTargetDefault: function () {
                    var targetIndex = targets.index(target) - 1;
                    if (targetIndex < 0) {
                        if (options.quitOnEnd === true) {
                            _quitImageLightbox();
                            return false;
                        }
                        else {
                            targetIndex = targets.length - 1;
                        }
                    }
                    target = targets.eq(targetIndex);
                },
                nextTarget: function () {
                    return this.nextTargetDefault();
                },
                nextTargetDefault: function () {
                    var targetIndex = targets.index(target) + 1;
                    if (targetIndex >= targets.length) {
                        if (options.quitOnEnd === true) {
                            _quitImageLightbox();
                            return false;
                        }
                        else {
                            targetIndex = 0;
                        }
                    }
                    target = targets.eq(targetIndex);
                }
            }, opts),
            activityIndicatorOn = function () {
                $wrapper.append($activityObject);
            },
            activityIndicatorOff = function () {
                $('#imagelightbox-loading').remove();
            },
            lockBody = function (toggle) {
                if (toggle) {
                    $("body").css("overflow","hidden");
                } else {
                    $("body").css("overflow","scroll");
                }
            },
            overlayOn = function () {
                $wrapper.append($overlayObject);
            },
            closeButtonOn = function () {
                $buttonObject.appendTo($wrapper).on('click', function () {
                    _quitImageLightbox();
                    return false;
                });
            },
            captionOn = function () {
                var description = $(target).find('img').attr('alt');
                if (description && description.length > 0) {
                    $wrapper.append($captionObject.text(description));
                }
            },
            captionOff = function () {
                $captionObject.remove();
            },
            navigationOn = function (instance, selector) {
                var images = $(selector);
                if (images.length) {
                    for (var i = 0; i < images.length; i++) {
                        $navObject.append($('<a/>',{href:'#'}));
                    }
                    $wrapper.append($navObject);
                    $navObject.on('click touchend', function () {
                        return false;
                    });
                    var navItems = $navObject.find('a');
                    navItems.on('click touchend', function () {
                        var $this = $(this);
                        if (images.eq($this.index()).attr('href') !== $('#imagelightbox').attr('src')) {
                            var tmpTarget = targets.eq($this.index());
                            if (tmpTarget.length) {
                                var currentIndex = targets.index(target);
                                target = tmpTarget;
                                _loadImage($this.index() < currentIndex ? 'left' : 'right');
                            }
                        }
                        navItems.removeClass('active');
                        navItems.eq($this.index()).addClass('active');
                        return false;
                    }).on('touchend', function () {
                        return false;
                    });
                }
            },
            navigationUpdate = function (selector) {
                var items = $navObject.find('a');
                items.removeClass('active');
                items.eq($(selector).filter('[href="' + $('#imagelightbox').attr('src') + '"]').index(selector)).addClass('active');
            },
            navigationOff = function () {
                $('#imagelightbox-nav').remove();
            },
            arrowsOn = function () {
                $wrapper.append($arrows);
                $arrows.on('click touchend', function (e) {
                    e.preventDefault();
                    if ($(this).hasClass('imagelightbox-arrow-left')) {
                        _loadPreviousImage();
                    } else {
                        _loadNextImage();
                    }
                    return false;
                });
            },
            arrowsOff = function () {
                $('.imagelightbox-arrow').remove();
            },

            targets = $([]),
            target = $(),
            image = $(),
            imageWidth = 0,
            imageHeight = 0,
            swipeDiff = 0,
            inProgress = false,

            /* TODO make it work again
             isTargetValid = function (element) {
             var classic = $(element).prop('tagName').toLowerCase() === 'a' && ( new RegExp('.(' + options.allowedTypes + ')$', 'i') ).test($(element).attr('href'));
             var html5 = $(element).attr('data-lightbox') !== undefined;
             return classic || html5;
             },
             */

            _setImage = function () {
                if (!image.length) {
                    return true;
                }

                var screenWidth = $(window).width() * 0.8,
                    wHeight = (window.innerHeight) ? window.innerHeight : $(window).height(),
                    screenHeight = wHeight * 0.9,
                    tmpImage = new Image();

                tmpImage.src = image.attr('src');
                tmpImage.onload = function () {
                    imageWidth = tmpImage.width;
                    imageHeight = tmpImage.height;

                    if (imageWidth > screenWidth || imageHeight > screenHeight) {
                        var ratio = imageWidth / imageHeight > screenWidth / screenHeight ? imageWidth / screenWidth : imageHeight / screenHeight;
                        imageWidth /= ratio;
                        imageHeight /= ratio;
                    }

                    image.css({
                        'width': imageWidth + 'px',
                        'height': imageHeight + 'px',
                        'top': ( wHeight - imageHeight ) / 2 + 'px',
                        'left': ( $(window).width() - imageWidth ) / 2 + 'px'
                    });
                };
            },

            _loadImage = function (direction) {
                if (inProgress) {
                    return false;
                }

                direction = typeof direction === 'undefined' ? false : direction === 'left' ? 1 : -1;

                if (image.length) {
                    var params = {'opacity': 0};
                    if (isCssTransitionSupport) {
                        cssTransitionTranslateX(image, ( 100 * direction ) - swipeDiff + 'px', options.animationSpeed / 1000);
                    }
                    else {
                        params.left = parseInt(image.css('left')) + 100 * direction + 'px';
                    }
                    image.animate(params, options.animationSpeed, function () {
                        _removeImage();
                    });
                    swipeDiff = 0;
                }

                inProgress = true;
                if (options.onLoadStart !== false) {
                    options.onLoadStart();
                }

                setTimeout(function () {
                    var imgPath = target.attr('href');
                    // if ( imgPath === undefined ) {
                    //     imgPath = target.attr( 'data-lightbox' );
                    // }
                    image = $('<img id="' + options.id + '" />')
                        .attr('src', imgPath)
                        .on('load', function () {
                            var params = {'opacity': 1};

                            image.appendTo($wrapper);
                            _setImage();
                            image.css('opacity', 0);
                            if (isCssTransitionSupport) {
                                cssTransitionTranslateX(image, -100 * direction + 'px', 0);
                                setTimeout(function () {
                                    cssTransitionTranslateX(image, 0 + 'px', options.animationSpeed / 1000);
                                }, 50);
                            } else {
                                var imagePosLeft = parseInt(image.css('left'));
                                params.left = imagePosLeft + 'px';
                                image.css('left', imagePosLeft - 100 * direction + 'px');
                            }

                            image.animate(params, options.animationSpeed, function () {
                                inProgress = false;
                                if (options.onLoadEnd !== false) {
                                    options.onLoadEnd();
                                }
                            });
                            if (options.preloadNext) {
                                var nextTarget = targets.eq(targets.index(target) + 1);
                                if (!nextTarget.length) {
                                    nextTarget = targets.eq(0);
                                }
                                $('<img />').attr('src', nextTarget.attr('href'));
                            }
                        })
                        .on('error', function () {
                            if (options.onLoadEnd !== false) {
                                options.onLoadEnd();
                            }
                        });

                    var swipeStart = 0,
                        swipeEnd = 0,
                        imagePosLeft = 0;

                    image.on(hasPointers ? 'pointerup MSPointerUp' : 'click', function (e) {
                        e.preventDefault();
                        if (options.quitOnImgClick) {
                            _quitImageLightbox();
                            return false;
                        }
                        if (wasTouched(e.originalEvent)) {
                            return true;
                        }
                        var posX = ( e.pageX || e.originalEvent.pageX ) - e.target.offsetLeft;
                        if (imageWidth / 2 > posX) {
                            _loadPreviousImage();
                        } else {
                            _loadNextImage();
                        }
                    })
                        .on('touchstart pointerdown MSPointerDown', function (e) {
                            if (!wasTouched(e.originalEvent) || options.quitOnImgClick) {
                                return true;
                            }
                            if (isCssTransitionSupport) {
                                imagePosLeft = parseInt(image.css('left'));
                            }
                            swipeStart = e.originalEvent.pageX || e.originalEvent.touches[0].pageX;
                        })
                        .on('touchmove pointermove MSPointerMove', function (e) {
                            if (!wasTouched(e.originalEvent) || options.quitOnImgClick) {
                                return true;
                            }
                            e.preventDefault();
                            swipeEnd = e.originalEvent.pageX || e.originalEvent.touches[0].pageX;
                            swipeDiff = swipeStart - swipeEnd;
                            if (isCssTransitionSupport) {
                                cssTransitionTranslateX(image, -swipeDiff + 'px', 0);
                            } else {
                                image.css('left', imagePosLeft - swipeDiff + 'px');
                            }
                        })
                        .on('touchend touchcancel pointerup pointercancel MSPointerUp MSPointerCancel', function (e) {
                            if (!wasTouched(e.originalEvent) || options.quitOnImgClick) {
                                return true;
                            }
                            if (Math.abs(swipeDiff) > 50) {
                                if (swipeDiff < 0) {
                                    _loadPreviousImage();
                                } else {
                                    _loadNextImage();
                                }
                            } else {
                                if (isCssTransitionSupport) {
                                    cssTransitionTranslateX(image, 0 + 'px', options.animationSpeed / 1000);
                                } else {
                                    image.animate({'left': imagePosLeft + 'px'}, options.animationSpeed / 2);
                                }
                            }
                        });

                }, options.animationSpeed + 100);
            },

            _loadPreviousImage = function () {
                if (options.previousTarget() !== false) {
                    _loadImage('left');
                }
            },

            _loadNextImage = function () {
                if (options.nextTarget() !== false) {
                    _loadImage('right');
                }
            },

            _removeImage = function () {
                if (!image.length) {
                    return false;
                }
                image.remove();
                image = $();
            },

            _openImageLightbox = function ($target) {
                if (inProgress) {
                    return false;
                }
                inProgress = false;
                if (options.onStart !== false) {
                    options.onStart();
                }
                $('body').append($wrapper);
                target = $target;
                _loadImage();
            },

            _quitImageLightbox = function () {
                if (!image.length) {
                    return false;
                }
                image.animate({'opacity': 0}, options.animationSpeed, function () {
                    _removeImage();
                    inProgress = false;
                    if (options.onEnd !== false) {
                        options.onEnd();
                    }
                });
            },

            _addTargets = function( newTargets ) {
                newTargets.each(function () {
                    targets = targets.add($(this));
                });

                newTargets.on('click', function (e) {
                    e.preventDefault();
                    _openImageLightbox($(this));
                });
            };

        this.startImageLightbox = function () {
            if (this.length > 0) {
                _openImageLightbox($(this[0]));
            }
        };

        $(window).on('resize', _setImage);

        $(document).ready(function() {
            if (options.quitOnDocClick) {
                $(document).on(hasTouch ? 'touchend' : 'click', function (e) {
                    if (image.length && !$(e.target).is(image)) {
                        e.preventDefault();
                        _quitImageLightbox();
                    }
                });
            }

            if (options.enableKeyboard) {
                $(document).on('keyup', function (e) {
                    if (!image.length) {
                        return true;
                    }
                    e.preventDefault();
                    if (e.keyCode === 27 && options.quitOnEscKey === true) {
                        _quitImageLightbox();
                    }
                    if (e.keyCode === 37) {
                        _loadPreviousImage();
                    } else if (e.keyCode === 39) {
                        _loadNextImage();
                    }
                });
            }
        });

        $(document).off('click', this.selector);

        _addTargets($(this));

        this.loadPreviousImage = function () {
            _loadPreviousImage();
        };

        this.loadNextImage = function () {
            _loadNextImage();
        };

        this.quitImageLightbox = function () {
            _quitImageLightbox();
            return this;
        };

        this.addToImageLightbox = function(elements)  {
            _addTargets(elements);
        };

        return this;
    };
})(jQuery, window, document);
<|endoftext|>


<commit_before>/**
 * Simple JSON serializer.
 *
 * @requires javelin-install javelin-util
 * @provides javelin-json
 * @javelin
 */

JX.install('JSON', {
  statics : {
    serialize : function(obj) {
      if (__DEV__) {
        try {
          return JX.JSON._val(obj);
        } catch (x) {
          JX.log(
            'JX.JSON.serialize(...): '+
            'caught exception while serializing object. ('+x+')');
        }
      } else {
        return JX.JSON._val(obj);
      }
    },
    _val : function(val) {
      var out = [];
      if (val === null) {
        return 'null';
      } else if (val.push && val.pop) {
        for (var ii = 0; ii < val.length; ii++) {
          if (typeof val[ii] != 'undefined') {
            out.push(JX.JSON._val(val[ii]));
          }
        }
        return '['+out.join(',')+']';
      } else if (val === true) {
        return 'true';
      } else if (val === false) {
        return 'false';
      } else if (typeof val == 'string') {
        return JX.JSON._esc(val);
      } else if (typeof val == 'number') {
        return val;
      } else {
        for (var k in val) {
          out.push(JX.JSON._esc(k)+':'+JX.JSON._val(val[k]));
        }
        return '{'+out.join(',')+'}';
      }
    },

    // Lifted more or less directly from Crockford's JSON2.
    _escexp : /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,

    _meta : {
      '\b' : '\\b',
      '\t' : '\\t',
      '\n' : '\\n',
      '\f' : '\\f',
      '\r' : '\\r',
      '"'  : '\\"',
      '\\' : '\\\\'
    },

    _esc : function(str) {
      JX.JSON._escexp.lastIndex = 0;
      return JX.JSON._escexp.test(str) ?
        '"' + str.replace(JX.JSON._escexp, JX.JSON._replace) + '"' :
        '"' + str + '"';
    },

    _replace : function(m) {
      if (m in JX.JSON._meta) {
        return JX.JSON._meta[m];
      }
      return '\\u' + (('0000' + m.charCodeAt(0).toString(16)).slice(-4));
    }

  }
});
<commit_msg>Simplify JSON code<commit_after>/**
 * Simple JSON serializer.
 *
 * @requires javelin-install javelin-util
 * @provides javelin-json
 * @javelin
 */

JX.install('JSON', {
  statics : {
    serialize : function(obj) {
      if (__DEV__) {
        try {
          return JX.JSON._val(obj);
        } catch (x) {
          JX.log(
            'JX.JSON.serialize(...): '+
            'caught exception while serializing object. ('+x+')');
        }
      } else {
        return JX.JSON._val(obj);
      }
    },
    _val : function(val) {
      var out = [];
      if (val === null || val === true || val === false || typeof val == 'number') {
        return ''+val;
      } else if (val.push && val.pop) {
        for (var ii = 0; ii < val.length; ii++) {
          if (typeof val[ii] != 'undefined') {
            out.push(JX.JSON._val(val[ii]));
          }
        }
        return '['+out.join(',')+']';
      } else if (typeof val == 'string') {
        return JX.JSON._esc(val);
      } else {
        for (var k in val) {
          out.push(JX.JSON._esc(k)+':'+JX.JSON._val(val[k]));
        }
        return '{'+out.join(',')+'}';
      }
    },

    // Lifted more or less directly from Crockford's JSON2.
    _escexp : /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,

    _meta : {
      '\b' : '\\b',
      '\t' : '\\t',
      '\n' : '\\n',
      '\f' : '\\f',
      '\r' : '\\r',
      '"'  : '\\"',
      '\\' : '\\\\'
    },

    _esc : function(str) {
      JX.JSON._escexp.lastIndex = 0;
      return JX.JSON._escexp.test(str) ?
        '"' + str.replace(JX.JSON._escexp, JX.JSON._replace) + '"' :
        '"' + str + '"';
    },

    _replace : function(m) {
      if (m in JX.JSON._meta) {
        return JX.JSON._meta[m];
      }
      return '\\u' + (('0000' + m.charCodeAt(0).toString(16)).slice(-4));
    }

  }
});
<|endoftext|>


<commit_before>/**
 * Copyright 2017, Google, Inc.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'use strict';

function deidentifyWithMask(
  callingProjectId,
  string,
  maskingCharacter,
  numberToMask
) {
  // [START dlp_deidentify_masking]
  // Imports the Google Cloud Data Loss Prevention library
  const DLP = require('@google-cloud/dlp');

  // Instantiates a client
  const dlp = new DLP.DlpServiceClient();

  // The project ID to run the API call under
  // const callingProjectId = process.env.GCLOUD_PROJECT;

  // The string to deidentify
  // const string = 'My SSN is 372819127';

  // (Optional) The maximum number of sensitive characters to mask in a match
  // If omitted from the request or set to 0, the API will mask any matching characters
  // const numberToMask = 5;

  // (Optional) The character to mask matching sensitive data with
  // const maskingCharacter = 'x';

  // Construct deidentification request
  const item = {value: string};
  const request = {
    parent: dlp.projectPath(callingProjectId),
    deidentifyConfig: {
      infoTypeTransformations: {
        transformations: [
          {
            primitiveTransformation: {
              characterMaskConfig: {
                maskingCharacter: maskingCharacter,
                numberToMask: numberToMask,
              },
            },
          },
        ],
      },
    },
    item: item,
  };

  // Run deidentification request
  dlp
    .deidentifyContent(request)
    .then(response => {
      const deidentifiedItem = response[0].item;
      console.log(deidentifiedItem.value);
    })
    .catch(err => {
      console.log(`Error in deidentifyWithMask: ${err.message || err}`);
    });
  // [END dlp_deidentify_masking]
}

function deidentifyWithDateShift(
  callingProjectId,
  inputCsvFile,
  outputCsvFile,
  dateFields,
  lowerBoundDays,
  upperBoundDays,
  contextFieldId,
  wrappedKey,
  keyName
) {
  // [START dlp_deidentify_date_shift]
  // Imports the Google Cloud Data Loss Prevention library
  const DLP = require('@google-cloud/dlp');

  // Instantiates a client
  const dlp = new DLP.DlpServiceClient();

  // Import other required libraries
  const fs = require('fs');

  // The project ID to run the API call under
  // const callingProjectId = process.env.GCLOUD_PROJECT;

  // The path to the CSV file to deidentify
  // The first row of the file must specify column names, and all other rows
  // must contain valid values
  // const inputCsvFile = '/path/to/input/file.csv';

  // The path to save the date-shifted CSV file to
  // const outputCsvFile = '/path/to/output/file.csv';

  // The list of (date) fields in the CSV file to date shift
  // const dateFields = [{ name: 'birth_date'}, { name: 'register_date' }];

  // The maximum number of days to shift a date backward
  // const lowerBoundDays = 1;

  // The maximum number of days to shift a date forward
  // const upperBoundDays = 1;

  // (Optional) The column to determine date shift amount based on
  // If this is not specified, a random shift amount will be used for every row
  // If this is specified, then 'wrappedKey' and 'keyName' must also be set
  // const contextFieldId = [{ name: 'user_id' }];

  // (Optional) The name of the Cloud KMS key used to encrypt ('wrap') the AES-256 key
  // If this is specified, then 'wrappedKey' and 'contextFieldId' must also be set
  // const keyName = 'projects/YOUR_GCLOUD_PROJECT/locations/YOUR_LOCATION/keyRings/YOUR_KEYRING_NAME/cryptoKeys/YOUR_KEY_NAME';

  // (Optional) The encrypted ('wrapped') AES-256 key to use when shifting dates
  // This key should be encrypted using the Cloud KMS key specified above
  // If this is specified, then 'keyName' and 'contextFieldId' must also be set
  // const wrappedKey = 'YOUR_ENCRYPTED_AES_256_KEY'

  // Helper function for converting CSV rows to Protobuf types
  const rowToProto = row => {
    const values = row.split(',');
    const convertedValues = values.map(value => {
      if (Date.parse(value)) {
        const date = new Date(value);
        return {
          dateValue: {
            year: date.getFullYear(),
            month: date.getMonth() + 1,
            day: date.getDate(),
          },
        };
      } else {
        // Convert all non-date values to strings
        return {stringValue: value.toString()};
      }
    });
    return {values: convertedValues};
  };

  // Read and parse a CSV file
  const csvLines = fs
    .readFileSync(inputCsvFile)
    .toString()
    .split('\n')
    .filter(line => line.includes(','));
  const csvHeaders = csvLines[0].split(',');
  const csvRows = csvLines.slice(1);

  // Construct the table object
  const tableItem = {
    table: {
      headers: csvHeaders.map(header => {
        return {name: header};
      }),
      rows: csvRows.map(row => rowToProto(row)),
    },
  };

  // Construct DateShiftConfig
  const dateShiftConfig = {
    lowerBoundDays: lowerBoundDays,
    upperBoundDays: upperBoundDays,
  };

  if (contextFieldId && keyName && wrappedKey) {
    dateShiftConfig.context = {name: contextFieldId};
    dateShiftConfig.cryptoKey = {
      kmsWrapped: {
        wrappedKey: wrappedKey,
        cryptoKeyName: keyName,
      },
    };
  } else if (contextFieldId || keyName || wrappedKey) {
    throw new Error(
      'You must set either ALL or NONE of {contextFieldId, keyName, wrappedKey}!'
    );
  }

  // Construct deidentification request
  const request = {
    parent: dlp.projectPath(callingProjectId),
    deidentifyConfig: {
      recordTransformations: {
        fieldTransformations: [
          {
            fields: dateFields,
            primitiveTransformation: {
              dateShiftConfig: dateShiftConfig,
            },
          },
        ],
      },
    },
    item: tableItem,
  };

  // Run deidentification request
  dlp
    .deidentifyContent(request)
    .then(response => {
      const tableRows = response[0].item.table.rows;

      // Write results to a CSV file
      tableRows.forEach((row, rowIndex) => {
        const rowValues = row.values.map(
          value =>
            value.stringValue ||
            `${value.dateValue.month}/${value.dateValue.day}/${
              value.dateValue.year
            }`
        );
        csvLines[rowIndex + 1] = rowValues.join(',');
      });
      csvLines.push('');
      fs.writeFileSync(outputCsvFile, csvLines.join('\n'));

      // Print status
      console.log(`Successfully saved date-shift output to ${outputCsvFile}`);
    })
    .catch(err => {
      console.log(`Error in deidentifyWithDateShift: ${err.message || err}`);
    });
  // [END deidentify_date_shift]
}

function deidentifyWithFpe(
  callingProjectId,
  string,
  alphabet,
  surrogateType,
  keyName,
  wrappedKey
) {
  // [START dlp_deidentify_fpe]
  // Imports the Google Cloud Data Loss Prevention library
  const DLP = require('@google-cloud/dlp');

  // Instantiates a client
  const dlp = new DLP.DlpServiceClient();

  // The project ID to run the API call under
  // const callingProjectId = process.env.GCLOUD_PROJECT;

  // The string to deidentify
  // const string = 'My SSN is 372819127';

  // The set of characters to replace sensitive ones with
  // For more information, see https://cloud.google.com/dlp/docs/reference/rest/v2/organizations.deidentifyTemplates#ffxcommonnativealphabet
  // const alphabet = 'ALPHA_NUMERIC';

  // The name of the Cloud KMS key used to encrypt ('wrap') the AES-256 key
  // const keyName = 'projects/YOUR_GCLOUD_PROJECT/locations/YOUR_LOCATION/keyRings/YOUR_KEYRING_NAME/cryptoKeys/YOUR_KEY_NAME';

  // The encrypted ('wrapped') AES-256 key to use
  // This key should be encrypted using the Cloud KMS key specified above
  // const wrappedKey = 'YOUR_ENCRYPTED_AES_256_KEY'

  // (Optional) The name of the surrogate custom info type to use
  // Only necessary if you want to reverse the deidentification process
  // Can be essentially any arbitrary string, as long as it doesn't appear
  // in your dataset otherwise.
  // const surrogateType = 'SOME_INFO_TYPE_DEID';

  // Construct FPE config
  const cryptoReplaceFfxFpeConfig = {
    cryptoKey: {
      kmsWrapped: {
        wrappedKey: wrappedKey,
        cryptoKeyName: keyName,
      },
    },
    commonAlphabet: alphabet,
  };
  if (surrogateType) {
    cryptoReplaceFfxFpeConfig.surrogateInfoType = {
      name: surrogateType,
    };
  }

  // Construct deidentification request
  const item = {value: string};
  const request = {
    parent: dlp.projectPath(callingProjectId),
    deidentifyConfig: {
      infoTypeTransformations: {
        transformations: [
          {
            primitiveTransformation: {
              cryptoReplaceFfxFpeConfig: cryptoReplaceFfxFpeConfig,
            },
          },
        ],
      },
    },
    item: item,
  };

  // Run deidentification request
  dlp
    .deidentifyContent(request)
    .then(response => {
      const deidentifiedItem = response[0].item;
      console.log(deidentifiedItem.value);
    })
    .catch(err => {
      console.log(`Error in deidentifyWithFpe: ${err.message || err}`);
    });
  // [END deidentify_fpe]
}

function reidentifyWithFpe(
  callingProjectId,
  string,
  alphabet,
  surrogateType,
  keyName,
  wrappedKey
) {
  // [START dlp_reidentify_fpe]
  // Imports the Google Cloud Data Loss Prevention library
  const DLP = require('@google-cloud/dlp');

  // Instantiates a client
  const dlp = new DLP.DlpServiceClient();

  // The project ID to run the API call under
  // const callingProjectId = process.env.GCLOUD_PROJECT;

  // The string to reidentify
  // const string = 'My SSN is PHONE_TOKEN(9):#########';

  // The set of characters to replace sensitive ones with
  // For more information, see https://cloud.google.com/dlp/docs/reference/rest/v2/organizations.deidentifyTemplates#ffxcommonnativealphabet
  // const alphabet = 'ALPHA_NUMERIC';

  // The name of the Cloud KMS key used to encrypt ('wrap') the AES-256 key
  // const keyName = 'projects/YOUR_GCLOUD_PROJECT/locations/YOUR_LOCATION/keyRings/YOUR_KEYRING_NAME/cryptoKeys/YOUR_KEY_NAME';

  // The encrypted ('wrapped') AES-256 key to use
  // This key should be encrypted using the Cloud KMS key specified above
  // const wrappedKey = 'YOUR_ENCRYPTED_AES_256_KEY'

  // The name of the surrogate custom info type to use when reidentifying data
  // const surrogateType = 'SOME_INFO_TYPE_DEID';

  // Construct deidentification request
  const item = {value: string};
  const request = {
    parent: dlp.projectPath(callingProjectId),
    reidentifyConfig: {
      infoTypeTransformations: {
        transformations: [
          {
            primitiveTransformation: {
              cryptoReplaceFfxFpeConfig: {
                cryptoKey: {
                  kmsWrapped: {
                    wrappedKey: wrappedKey,
                    cryptoKeyName: keyName,
                  },
                },
                commonAlphabet: alphabet,
                surrogateInfoType: {
                  name: surrogateType,
                },
              },
            },
          },
        ],
      },
    },
    inspectConfig: {
      customInfoTypes: [
        {
          infoType: {
            name: surrogateType,
          },
          surrogateType: {},
        },
      ],
    },
    item: item,
  };

  // Run reidentification request
  dlp
    .reidentifyContent(request)
    .then(response => {
      const reidentifiedItem = response[0].item;
      console.log(reidentifiedItem.value);
    })
    .catch(err => {
      console.log(`Error in reidentifyWithFpe: ${err.message || err}`);
    });
  // [END dlp_reidentify_fpe]
}

const cli = require(`yargs`)
  .demand(1)
  .command(
    `deidMask <string>`,
    `Deidentify sensitive data in a string by masking it with a character.`,
    {
      maskingCharacter: {
        type: 'string',
        alias: 'm',
        default: '',
      },
      numberToMask: {
        type: 'number',
        alias: 'n',
        default: 0,
      },
    },
    opts =>
      deidentifyWithMask(
        opts.callingProjectId,
        opts.string,
        opts.maskingCharacter,
        opts.numberToMask
      )
  )
  .command(
    `deidFpe <string> <wrappedKey> <keyName>`,
    `Deidentify sensitive data in a string using Format Preserving Encryption (FPE).`,
    {
      alphabet: {
        type: 'string',
        alias: 'a',
        default: 'ALPHA_NUMERIC',
        choices: [
          'NUMERIC',
          'HEXADECIMAL',
          'UPPER_CASE_ALPHA_NUMERIC',
          'ALPHA_NUMERIC',
        ],
      },
      surrogateType: {
        type: 'string',
        alias: 's',
        default: '',
      },
    },
    opts =>
      deidentifyWithFpe(
        opts.callingProjectId,
        opts.string,
        opts.alphabet,
        opts.surrogateType,
        opts.keyName,
        opts.wrappedKey
      )
  )
  .command(
    `reidFpe <string> <surrogateType> <wrappedKey> <keyName>`,
    `Reidentify sensitive data in a string using Format Preserving Encryption (FPE).`,
    {
      alphabet: {
        type: 'string',
        alias: 'a',
        default: 'ALPHA_NUMERIC',
        choices: [
          'NUMERIC',
          'HEXADECIMAL',
          'UPPER_CASE_ALPHA_NUMERIC',
          'ALPHA_NUMERIC',
        ],
      },
    },
    opts =>
      reidentifyWithFpe(
        opts.callingProjectId,
        opts.string,
        opts.alphabet,
        opts.surrogateType,
        opts.keyName,
        opts.wrappedKey
      )
  )
  .command(
    `deidDateShift <inputCsvFile> <outputCsvFile> <lowerBoundDays> <upperBoundDays> [dateFields...]`,
    `Deidentify dates in a CSV file by pseudorandomly shifting them.`,
    {
      contextFieldId: {
        type: 'string',
        alias: 'f',
        default: '',
      },
      wrappedKey: {
        type: 'string',
        alias: 'w',
        default: '',
      },
      keyName: {
        type: 'string',
        alias: 'n',
        default: '',
      },
    },
    opts =>
      deidentifyWithDateShift(
        opts.callingProjectId,
        opts.inputCsvFile,
        opts.outputCsvFile,
        opts.dateFields.map(f => {
          return {name: f};
        }),
        opts.lowerBoundDays,
        opts.upperBoundDays,
        opts.contextFieldId,
        opts.wrappedKey,
        opts.keyName
      )
  )
  .option('c', {
    type: 'string',
    alias: 'callingProjectId',
    default: process.env.GCLOUD_PROJECT || '',
  })
  .example(`node $0 deidMask "My SSN is 372819127"`)
  .example(
    `node $0 deidFpe "My SSN is 372819127" <YOUR_ENCRYPTED_AES_256_KEY> projects/my-project/locations/global/keyrings/my-keyring -s SSN_TOKEN`
  )
  .example(
    `node $0 reidFpe "My SSN is SSN_TOKEN(9):#########" <YOUR_ENCRYPTED_AES_256_KEY> projects/my-project/locations/global/keyrings/my-keyring SSN_TOKEN -a NUMERIC`
  )
  .example(
    `node $0 deidDateShift dates.csv dates-shifted.csv 30 30 birth_date register_date [-w <YOUR_ENCRYPTED_AES_256_KEY> -n projects/my-project/locations/global/keyrings/my-keyring]`
  )
  .wrap(120)
  .recommendCommands()
  .epilogue(`For more information, see https://cloud.google.com/dlp/docs.`);

if (module === require.main) {
  cli.help().strict().argv; // eslint-disable-line
}
<commit_msg>Fix dlp code sample end/start tags for embedding (#76)<commit_after>/**
 * Copyright 2017, Google, Inc.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'use strict';

function deidentifyWithMask(
  callingProjectId,
  string,
  maskingCharacter,
  numberToMask
) {
  // [START dlp_deidentify_masking]
  // Imports the Google Cloud Data Loss Prevention library
  const DLP = require('@google-cloud/dlp');

  // Instantiates a client
  const dlp = new DLP.DlpServiceClient();

  // The project ID to run the API call under
  // const callingProjectId = process.env.GCLOUD_PROJECT;

  // The string to deidentify
  // const string = 'My SSN is 372819127';

  // (Optional) The maximum number of sensitive characters to mask in a match
  // If omitted from the request or set to 0, the API will mask any matching characters
  // const numberToMask = 5;

  // (Optional) The character to mask matching sensitive data with
  // const maskingCharacter = 'x';

  // Construct deidentification request
  const item = {value: string};
  const request = {
    parent: dlp.projectPath(callingProjectId),
    deidentifyConfig: {
      infoTypeTransformations: {
        transformations: [
          {
            primitiveTransformation: {
              characterMaskConfig: {
                maskingCharacter: maskingCharacter,
                numberToMask: numberToMask,
              },
            },
          },
        ],
      },
    },
    item: item,
  };

  // Run deidentification request
  dlp
    .deidentifyContent(request)
    .then(response => {
      const deidentifiedItem = response[0].item;
      console.log(deidentifiedItem.value);
    })
    .catch(err => {
      console.log(`Error in deidentifyWithMask: ${err.message || err}`);
    });
  // [END dlp_deidentify_masking]
}

function deidentifyWithDateShift(
  callingProjectId,
  inputCsvFile,
  outputCsvFile,
  dateFields,
  lowerBoundDays,
  upperBoundDays,
  contextFieldId,
  wrappedKey,
  keyName
) {
  // [START dlp_deidentify_date_shift]
  // Imports the Google Cloud Data Loss Prevention library
  const DLP = require('@google-cloud/dlp');

  // Instantiates a client
  const dlp = new DLP.DlpServiceClient();

  // Import other required libraries
  const fs = require('fs');

  // The project ID to run the API call under
  // const callingProjectId = process.env.GCLOUD_PROJECT;

  // The path to the CSV file to deidentify
  // The first row of the file must specify column names, and all other rows
  // must contain valid values
  // const inputCsvFile = '/path/to/input/file.csv';

  // The path to save the date-shifted CSV file to
  // const outputCsvFile = '/path/to/output/file.csv';

  // The list of (date) fields in the CSV file to date shift
  // const dateFields = [{ name: 'birth_date'}, { name: 'register_date' }];

  // The maximum number of days to shift a date backward
  // const lowerBoundDays = 1;

  // The maximum number of days to shift a date forward
  // const upperBoundDays = 1;

  // (Optional) The column to determine date shift amount based on
  // If this is not specified, a random shift amount will be used for every row
  // If this is specified, then 'wrappedKey' and 'keyName' must also be set
  // const contextFieldId = [{ name: 'user_id' }];

  // (Optional) The name of the Cloud KMS key used to encrypt ('wrap') the AES-256 key
  // If this is specified, then 'wrappedKey' and 'contextFieldId' must also be set
  // const keyName = 'projects/YOUR_GCLOUD_PROJECT/locations/YOUR_LOCATION/keyRings/YOUR_KEYRING_NAME/cryptoKeys/YOUR_KEY_NAME';

  // (Optional) The encrypted ('wrapped') AES-256 key to use when shifting dates
  // This key should be encrypted using the Cloud KMS key specified above
  // If this is specified, then 'keyName' and 'contextFieldId' must also be set
  // const wrappedKey = 'YOUR_ENCRYPTED_AES_256_KEY'

  // Helper function for converting CSV rows to Protobuf types
  const rowToProto = row => {
    const values = row.split(',');
    const convertedValues = values.map(value => {
      if (Date.parse(value)) {
        const date = new Date(value);
        return {
          dateValue: {
            year: date.getFullYear(),
            month: date.getMonth() + 1,
            day: date.getDate(),
          },
        };
      } else {
        // Convert all non-date values to strings
        return {stringValue: value.toString()};
      }
    });
    return {values: convertedValues};
  };

  // Read and parse a CSV file
  const csvLines = fs
    .readFileSync(inputCsvFile)
    .toString()
    .split('\n')
    .filter(line => line.includes(','));
  const csvHeaders = csvLines[0].split(',');
  const csvRows = csvLines.slice(1);

  // Construct the table object
  const tableItem = {
    table: {
      headers: csvHeaders.map(header => {
        return {name: header};
      }),
      rows: csvRows.map(row => rowToProto(row)),
    },
  };

  // Construct DateShiftConfig
  const dateShiftConfig = {
    lowerBoundDays: lowerBoundDays,
    upperBoundDays: upperBoundDays,
  };

  if (contextFieldId && keyName && wrappedKey) {
    dateShiftConfig.context = {name: contextFieldId};
    dateShiftConfig.cryptoKey = {
      kmsWrapped: {
        wrappedKey: wrappedKey,
        cryptoKeyName: keyName,
      },
    };
  } else if (contextFieldId || keyName || wrappedKey) {
    throw new Error(
      'You must set either ALL or NONE of {contextFieldId, keyName, wrappedKey}!'
    );
  }

  // Construct deidentification request
  const request = {
    parent: dlp.projectPath(callingProjectId),
    deidentifyConfig: {
      recordTransformations: {
        fieldTransformations: [
          {
            fields: dateFields,
            primitiveTransformation: {
              dateShiftConfig: dateShiftConfig,
            },
          },
        ],
      },
    },
    item: tableItem,
  };

  // Run deidentification request
  dlp
    .deidentifyContent(request)
    .then(response => {
      const tableRows = response[0].item.table.rows;

      // Write results to a CSV file
      tableRows.forEach((row, rowIndex) => {
        const rowValues = row.values.map(
          value =>
            value.stringValue ||
            `${value.dateValue.month}/${value.dateValue.day}/${
              value.dateValue.year
            }`
        );
        csvLines[rowIndex + 1] = rowValues.join(',');
      });
      csvLines.push('');
      fs.writeFileSync(outputCsvFile, csvLines.join('\n'));

      // Print status
      console.log(`Successfully saved date-shift output to ${outputCsvFile}`);
    })
    .catch(err => {
      console.log(`Error in deidentifyWithDateShift: ${err.message || err}`);
    });
  // [END dlp_deidentify_date_shift]
}

function deidentifyWithFpe(
  callingProjectId,
  string,
  alphabet,
  surrogateType,
  keyName,
  wrappedKey
) {
  // [START dlp_deidentify_fpe]
  // Imports the Google Cloud Data Loss Prevention library
  const DLP = require('@google-cloud/dlp');

  // Instantiates a client
  const dlp = new DLP.DlpServiceClient();

  // The project ID to run the API call under
  // const callingProjectId = process.env.GCLOUD_PROJECT;

  // The string to deidentify
  // const string = 'My SSN is 372819127';

  // The set of characters to replace sensitive ones with
  // For more information, see https://cloud.google.com/dlp/docs/reference/rest/v2/organizations.deidentifyTemplates#ffxcommonnativealphabet
  // const alphabet = 'ALPHA_NUMERIC';

  // The name of the Cloud KMS key used to encrypt ('wrap') the AES-256 key
  // const keyName = 'projects/YOUR_GCLOUD_PROJECT/locations/YOUR_LOCATION/keyRings/YOUR_KEYRING_NAME/cryptoKeys/YOUR_KEY_NAME';

  // The encrypted ('wrapped') AES-256 key to use
  // This key should be encrypted using the Cloud KMS key specified above
  // const wrappedKey = 'YOUR_ENCRYPTED_AES_256_KEY'

  // (Optional) The name of the surrogate custom info type to use
  // Only necessary if you want to reverse the deidentification process
  // Can be essentially any arbitrary string, as long as it doesn't appear
  // in your dataset otherwise.
  // const surrogateType = 'SOME_INFO_TYPE_DEID';

  // Construct FPE config
  const cryptoReplaceFfxFpeConfig = {
    cryptoKey: {
      kmsWrapped: {
        wrappedKey: wrappedKey,
        cryptoKeyName: keyName,
      },
    },
    commonAlphabet: alphabet,
  };
  if (surrogateType) {
    cryptoReplaceFfxFpeConfig.surrogateInfoType = {
      name: surrogateType,
    };
  }

  // Construct deidentification request
  const item = {value: string};
  const request = {
    parent: dlp.projectPath(callingProjectId),
    deidentifyConfig: {
      infoTypeTransformations: {
        transformations: [
          {
            primitiveTransformation: {
              cryptoReplaceFfxFpeConfig: cryptoReplaceFfxFpeConfig,
            },
          },
        ],
      },
    },
    item: item,
  };

  // Run deidentification request
  dlp
    .deidentifyContent(request)
    .then(response => {
      const deidentifiedItem = response[0].item;
      console.log(deidentifiedItem.value);
    })
    .catch(err => {
      console.log(`Error in deidentifyWithFpe: ${err.message || err}`);
    });
  // [END dlp_deidentify_fpe]
}

function reidentifyWithFpe(
  callingProjectId,
  string,
  alphabet,
  surrogateType,
  keyName,
  wrappedKey
) {
  // [START dlp_reidentify_fpe]
  // Imports the Google Cloud Data Loss Prevention library
  const DLP = require('@google-cloud/dlp');

  // Instantiates a client
  const dlp = new DLP.DlpServiceClient();

  // The project ID to run the API call under
  // const callingProjectId = process.env.GCLOUD_PROJECT;

  // The string to reidentify
  // const string = 'My SSN is PHONE_TOKEN(9):#########';

  // The set of characters to replace sensitive ones with
  // For more information, see https://cloud.google.com/dlp/docs/reference/rest/v2/organizations.deidentifyTemplates#ffxcommonnativealphabet
  // const alphabet = 'ALPHA_NUMERIC';

  // The name of the Cloud KMS key used to encrypt ('wrap') the AES-256 key
  // const keyName = 'projects/YOUR_GCLOUD_PROJECT/locations/YOUR_LOCATION/keyRings/YOUR_KEYRING_NAME/cryptoKeys/YOUR_KEY_NAME';

  // The encrypted ('wrapped') AES-256 key to use
  // This key should be encrypted using the Cloud KMS key specified above
  // const wrappedKey = 'YOUR_ENCRYPTED_AES_256_KEY'

  // The name of the surrogate custom info type to use when reidentifying data
  // const surrogateType = 'SOME_INFO_TYPE_DEID';

  // Construct deidentification request
  const item = {value: string};
  const request = {
    parent: dlp.projectPath(callingProjectId),
    reidentifyConfig: {
      infoTypeTransformations: {
        transformations: [
          {
            primitiveTransformation: {
              cryptoReplaceFfxFpeConfig: {
                cryptoKey: {
                  kmsWrapped: {
                    wrappedKey: wrappedKey,
                    cryptoKeyName: keyName,
                  },
                },
                commonAlphabet: alphabet,
                surrogateInfoType: {
                  name: surrogateType,
                },
              },
            },
          },
        ],
      },
    },
    inspectConfig: {
      customInfoTypes: [
        {
          infoType: {
            name: surrogateType,
          },
          surrogateType: {},
        },
      ],
    },
    item: item,
  };

  // Run reidentification request
  dlp
    .reidentifyContent(request)
    .then(response => {
      const reidentifiedItem = response[0].item;
      console.log(reidentifiedItem.value);
    })
    .catch(err => {
      console.log(`Error in reidentifyWithFpe: ${err.message || err}`);
    });
  // [END dlp_reidentify_fpe]
}

const cli = require(`yargs`)
  .demand(1)
  .command(
    `deidMask <string>`,
    `Deidentify sensitive data in a string by masking it with a character.`,
    {
      maskingCharacter: {
        type: 'string',
        alias: 'm',
        default: '',
      },
      numberToMask: {
        type: 'number',
        alias: 'n',
        default: 0,
      },
    },
    opts =>
      deidentifyWithMask(
        opts.callingProjectId,
        opts.string,
        opts.maskingCharacter,
        opts.numberToMask
      )
  )
  .command(
    `deidFpe <string> <wrappedKey> <keyName>`,
    `Deidentify sensitive data in a string using Format Preserving Encryption (FPE).`,
    {
      alphabet: {
        type: 'string',
        alias: 'a',
        default: 'ALPHA_NUMERIC',
        choices: [
          'NUMERIC',
          'HEXADECIMAL',
          'UPPER_CASE_ALPHA_NUMERIC',
          'ALPHA_NUMERIC',
        ],
      },
      surrogateType: {
        type: 'string',
        alias: 's',
        default: '',
      },
    },
    opts =>
      deidentifyWithFpe(
        opts.callingProjectId,
        opts.string,
        opts.alphabet,
        opts.surrogateType,
        opts.keyName,
        opts.wrappedKey
      )
  )
  .command(
    `reidFpe <string> <surrogateType> <wrappedKey> <keyName>`,
    `Reidentify sensitive data in a string using Format Preserving Encryption (FPE).`,
    {
      alphabet: {
        type: 'string',
        alias: 'a',
        default: 'ALPHA_NUMERIC',
        choices: [
          'NUMERIC',
          'HEXADECIMAL',
          'UPPER_CASE_ALPHA_NUMERIC',
          'ALPHA_NUMERIC',
        ],
      },
    },
    opts =>
      reidentifyWithFpe(
        opts.callingProjectId,
        opts.string,
        opts.alphabet,
        opts.surrogateType,
        opts.keyName,
        opts.wrappedKey
      )
  )
  .command(
    `deidDateShift <inputCsvFile> <outputCsvFile> <lowerBoundDays> <upperBoundDays> [dateFields...]`,
    `Deidentify dates in a CSV file by pseudorandomly shifting them.`,
    {
      contextFieldId: {
        type: 'string',
        alias: 'f',
        default: '',
      },
      wrappedKey: {
        type: 'string',
        alias: 'w',
        default: '',
      },
      keyName: {
        type: 'string',
        alias: 'n',
        default: '',
      },
    },
    opts =>
      deidentifyWithDateShift(
        opts.callingProjectId,
        opts.inputCsvFile,
        opts.outputCsvFile,
        opts.dateFields.map(f => {
          return {name: f};
        }),
        opts.lowerBoundDays,
        opts.upperBoundDays,
        opts.contextFieldId,
        opts.wrappedKey,
        opts.keyName
      )
  )
  .option('c', {
    type: 'string',
    alias: 'callingProjectId',
    default: process.env.GCLOUD_PROJECT || '',
  })
  .example(`node $0 deidMask "My SSN is 372819127"`)
  .example(
    `node $0 deidFpe "My SSN is 372819127" <YOUR_ENCRYPTED_AES_256_KEY> projects/my-project/locations/global/keyrings/my-keyring -s SSN_TOKEN`
  )
  .example(
    `node $0 reidFpe "My SSN is SSN_TOKEN(9):#########" <YOUR_ENCRYPTED_AES_256_KEY> projects/my-project/locations/global/keyrings/my-keyring SSN_TOKEN -a NUMERIC`
  )
  .example(
    `node $0 deidDateShift dates.csv dates-shifted.csv 30 30 birth_date register_date [-w <YOUR_ENCRYPTED_AES_256_KEY> -n projects/my-project/locations/global/keyrings/my-keyring]`
  )
  .wrap(120)
  .recommendCommands()
  .epilogue(`For more information, see https://cloud.google.com/dlp/docs.`);

if (module === require.main) {
  cli.help().strict().argv; // eslint-disable-line
}
<|endoftext|>