<commit_before>export default [
    {
        props: [
            {
                name: 'defaultIconPack',
                description: `Icon pack used internally and on the Icon component attribute —
                    Material Design Icons or
                    FontAwesome 4 or
                    FontAwesome 5`,
                type: 'String',
                values: 'mdi, fa, fas, far, fad',
                default: 'mdi'
            },
            {
                name: 'defaultIconComponent',
                description: `Component used to render the Icon.
                    Can be used to render FontAwesome 5 icons with the
                    vue-fontawesome component`,
                type: 'Component',
                values: 'FontAwesomeIcon component name',
                default: ''
            },
            {
                name: 'defaultStatusIcon',
                description: `Automatically show status icon on input component`,
                type: 'Boolean',
                values: '—',
                default: 'true'
            },
            {
                name: 'defaultIconPrev',
                description: `Icon used internally for prev. —
                    Used in Datepicker, Pagination and Steps for example`,
                type: 'String',
                values: '—',
                default: 'chevron-left'
            },
            {
                name: 'defaultIconNext',
                description: `Icon used internally for next. —
                    Used in Datepicker, Pagination and Steps for example`,
                type: 'String',
                values: '—',
                default: 'chevron-right'
            },
            {
                name: 'defaultLocale',
                description: `Will be used for default date and number formatting. Accept a string with a BCP 47 language tag, or an array of such strings.
                See Unicode BCP 47 locale identifier`,
                type: 'String, Array of String',
                values: '—',
                default: 'undefined: default to browser locale.'
            },
            {
                name: 'defaultContainerElement</>',
                description: `Default container attribute for floating Notices (Toasts & Snackbars). Note that this also
                    changes the position of the Notices from fixed to absolute.
                    Meaning that the container should be fixed.`,
                type: 'String',
                values: '—',
                default: 'body'
            },
            {
                name: 'defaultDialogConfirmText',
                description: `Default dialog <code>confirmText</code> attribute`,
                type: 'String',
                values: '—',
                default: 'OK'
            },
            {
                name: 'defaultDialogCancelText',
                description: `Default dialog <code>cancelText</code> attribute`,
                type: 'String',
                values: '—',
                default: 'Cancel'
            },
            {
                name: 'defaultSnackbarDuration',
                description: `Default snackbar <code>duration</code> attribute.`,
                type: 'Number',
                values: '—',
                default: '3500'
            },
            {
                name: 'defaultSnackbarPosition',
                description: `Default snackbar <code>position</code> attribute.`,
                type: 'String',
                values: `is-top-right, is-top, is-top-left,
                    is-bottom-right, is-bottom, is-bottom-left`,
                default: 'is-bottom-right'
            },
            {
                name: 'defaultToastDuration',
                description: `Default toast <code>duration</code> attribute.`,
                type: 'Number',
                values: '—',
                default: '2000'
            },
            {
                name: 'defaultToastPosition',
                description: `Default toast <code>position</code> attribute.`,
                type: 'String',
                values: `is-top-right, is-top, is-top-left,
                    is-bottom-right, is-bottom, is-bottom-left`,
                default: 'is-top'
            },
            {
                name: 'defaultNotificationDuration',
                description: `Default notification <code>duration</code> attribute.`,
                type: 'Number',
                values: '—',
                default: '2000'
            },
            {
                name: 'defaultNotificationPosition',
                description: `Default notification <code>position</code> attribute for programmatically opening.`,
                type: 'String',
                values: `is-top-right, is-top, is-top-left,
                    is-bottom-right, is-bottom, is-bottom-left`,
                default: 'is-bottom-right'
            },
            {
                name: 'defaultTooltipType',
                description: `Default tooltip type (color) attribute.`,
                type: 'String',
                values: `is-white, is-black, is-light,
                    is-dark, is-primary, is-info, is-success,
                    is-warning, is-danger,
                    and any other colors you've set in the $colors list on Sass`,
                default: 'is-primary'
            },
            {
                name: 'defaultTooltipDelay',
                description: `Default tooltip delay attribute.`,
                type: 'Number',
                values: `-`,
                default: '-'
            },
            {
                name: 'defaultInputAutocomplete',
                description: `Default input <code>autocomplete</code> attribute.`,
                type: 'String',
                values: 'on, off',
                default: 'on'
            },
            {
                name: 'defaultDateFormatter',
                description: `Default datepicker <code>date-formatter</code> attribute`,
                type: 'Function',
                values: '—',
                default: '(date) => new Intl.DateTimeFormat(locale, { timezome: "UTC" }).format(date)'
            },
            {
                name: 'defaultDateParser',
                description: `Default datepicker <code>date-parser</code> attribute`,
                type: 'Function',
                values: '—',
                default: 'Tries to parse the date using the locale specific format. Fallback to Date.parse'
            },
            {
                name: 'defaultDateCreator',
                description: `Default datepicker <code>date-creator</code> attribute`,
                type: 'Function',
                values: '—',
                default: 'new Date()'
            },
            {
                name: 'defaultDayNames',
                description: `Default datepicker <code>day-names</code> attribute`,
                type: 'Array',
                values: '—',
                default: `default to browser locale`
            },
            {
                name: 'defaultMonthNames',
                description: `Default datepicker <code>month-names</code> attribute`,
                type: 'Array',
                values: '—',
                default: `default to browser locale`
            },
            {
                name: 'defaultFirstDayOfWeek',
                description: `Default datepicker <code>first-day-of-week</code> attribute`,
                type: 'Number',
                values: '—',
                default: '0'
            },
            {
                name: 'defaultUnselectableDaysOfWeek',
                description: `Default datepicker <code>unselectable-days-of-week</code> attribute`,
                type: 'Array',
                values: '—',
                default: '—'
            },
            {
                name: 'defaultTimeFormatter',
                description: `Default timepicker <code>time-formatter</code> attribute`,
                type: 'Function',
                values: '—',
                default: 'HH:mm or HH:mm AM/PM'
            },
            {
                name: 'defaultTimeParser',
                description: `Default timepicker <code>time-parser</code> attribute`,
                type: 'Function',
                values: '—',
                default: 'HH:mm or HH:mm AM/PM'
            },
            {
                name: 'defaultModalCanCancel',
                description: `Default modal <code>canCancel</code> attribute`,
                type: 'Boolean, Array',
                values: 'escape, x, outside, button',
                default: `<code>['escape', 'x', 'outside', 'button']</code>`
            },
            {
                name: 'defaultModalScroll',
                description: `Default modal/dialog <code>scroll</code> attribute`,
                type: 'String',
                values: 'clip, keep',
                default: 'clip'
            },
            {
                name: 'defaultDatepickerMobileNative',
                description: 'Default native datepicker on mobile',
                type: 'Boolean',
                values: '—',
                default: 'true'
            },
            {
                name: 'defaultTimepickerMobileNative',
                description: 'Default native timepicker on mobile',
                type: 'Boolean',
                values: '—',
                default: 'true'
            },
            {
                name: 'defaultNoticeQueue',
                description: 'Default snackbar/toast/notification queue attribute',
                type: 'Boolean',
                values: '—',
                default: 'true'
            },
            {
                name: 'defaultInputHasCounter',
                description: 'Default input has-counter attribute',
                type: 'Boolean',
                values: '—',
                default: 'true'
            },
            {
                name: 'defaultTaginputHasCounter',
                description: 'Default taginput has-counter attribute',
                type: 'Boolean',
                values: '—',
                default: 'true'
            },
            {
                name: 'defaultUseHtml5Validation',
                description: 'Default form components use-html5-validation attribute',
                type: 'Boolean',
                values: '—',
                default: 'true'
            },
            {
                name: 'defaultFieldLabelPosition',
                description: 'Default field position of label',
                type: 'String',
                values: 'inside,on-border',
                default: '—'
            },
            {
                name: 'defaultDatepickerYearsRange',
                description: 'Default years range relative to selected year',
                type: 'Array',
                values: '—',
                default: '[-100, 3]'
            },
            {
                name: 'defaultDatepickerNearbyMonthDays',
                description: 'Show/Hide nearby month days (prev and next month)',
                type: 'Boolean',
                values: '—',
                default: 'true'
            },
            {
                name: 'defaultDatepickerNearbySelectableMonthDays',
                description: 'It allows to select/unselect nearby month days',
                type: 'Boolean',
                values: '—',
                default: 'false'
            },
            {
                name: 'defaultDatepickerShowWeekNumber',
                description: 'It allows to display week number',
                type: 'Boolean',
                values: '—',
                default: 'false'
            },
            {
                name: 'customIconPacks',
                description: 'Allows you to define your own custom icon pack to be used in Buefy.',
                type: 'Object',
                values: '—',
                default: '—'
            },
            {
                name: 'defaultClockpickerHoursLabel',
                description: `Default clockpicker <code>hours-label</code> attribute`,
                type: 'String',
                values: '—',
                default: `<code>Hours</code>`
            },
            {
                name: 'defaultClockpickerMinutesLabel',
                description: `Default clockpicker <code>minutes-label</code> attribute`,
                type: 'String',
                values: '—',
                default: `<code>Min</code>`
            },
            {
                name: 'defaultTrapFocus',
                description: 'Default config to trap focus inside components (Dialog, Dropdown and Modal).',
                type: 'Boolean',
                values: '—',
                default: 'false'
            },
            {
                name: 'defaultButtonRounded',
                description: 'Default config to make all buttons rounded.',
                type: 'Boolean',
                values: '—',
                default: 'false'
            },
            {
                name: 'defaultCarouselInterval',
                description: 'Default carousel interval attribute',
                type: 'Number',
                values: '—',
                default: '3500'
            },
            {
                name: 'defaultTabsExpanded',
                description: 'Default tabs expanded attribute',
                type: 'Boolean',
                values: '—',
                default: 'false'
            },
            {
                name: 'defaultTabsAnimated',
                description: 'Default tabs animated attribute',
                type: 'Boolean',
                values: '—',
                default: 'true'
            },
            {
                name: 'defaultTabsType',
                description: 'Default tabs type attribute',
                type: 'String',
                values: 'is-boxed, is-toggle',
                default: '—'
            },
            {
                name: 'defaultProgrammaticPromise',
                description: `Return a promise using programmatic component`,
                type: 'Boolean',
                values: '—',
                default: 'false'
            },
            {
                name: 'defaultLinkTags',
                description: 'Default link tags accepted as tag in some component (Button, MenuItem, PaginationButton). Can be used when using custom component or other Vue Router plugin like inertia.',
                type: 'string',
                values: '—',
                default: '[a, button, input, router-link, nuxt-link, n-link, RouterLink, NuxtLink, NLink]'
            },
            {
                name: 'defaultImageWebpFallback',
                description: `Fallback when using webp format. You can specify an extension only (.jpg, .jpeg, .png, .gif) if the filename is the same. You can use a full url if not.`,
                type: 'String',
                values: '—',
                default: '—'
            },
            {
                name: 'defaultImageLazy',
                description: `Use IntersectionObserver to display the image only when in viewport.`,
                type: 'Boolean',
                values: '—',
                default: 'true'
            },
            {
                name: 'defaultImageResponsive',
                description: `The image will take 100% of the parent width. Use this with <code>ratio</code> to prevent page jump when images are loading.`,
                type: 'Boolean',
                values: '—',
                default: 'true'
            },
            {
                name: 'defaultImageRatio',
                description: `The space will be reserved. Prevent page jump when images are loading when using <code>responsive</code>.`,
                type: 'String',
                values: '1by1, 5by4, 4by3, 3by2, 5by3, 16by9, 2by1, 3by1, 4by5, 3by4, 2by3, 3by5, 9by16, 1by2 1by3 or any string having this format {number}by{number}',
                default: '—'
            },
            {
                name: 'defaultImageSrcsetFormatter',
                description: `Function to format src according to a given size.`,
                type: 'Function',
                values: '—',
                default: '(src, size) => `${srcFilename}-${size}.${srcExt}`'
            }
        ]
    }
]
<commit_msg>Fix documentation for default trap-focus value (#2892)<commit_after>export default [
    {
        props: [
            {
                name: 'defaultIconPack',
                description: `Icon pack used internally and on the Icon component attribute —
                    Material Design Icons or
                    FontAwesome 4 or
                    FontAwesome 5`,
                type: 'String',
                values: 'mdi, fa, fas, far, fad',
                default: 'mdi'
            },
            {
                name: 'defaultIconComponent',
                description: `Component used to render the Icon.
                    Can be used to render FontAwesome 5 icons with the
                    vue-fontawesome component`,
                type: 'Component',
                values: 'FontAwesomeIcon component name',
                default: '—'
            },
            {
                name: 'defaultStatusIcon',
                description: `Automatically show status icon on input component`,
                type: 'Boolean',
                values: '—',
                default: 'true'
            },
            {
                name: 'defaultIconPrev',
                description: `Icon used internally for prev. —
                    Used in Datepicker, Pagination and Steps for example`,
                type: 'String',
                values: '—',
                default: 'chevron-left'
            },
            {
                name: 'defaultIconNext',
                description: `Icon used internally for next. —
                    Used in Datepicker, Pagination and Steps for example`,
                type: 'String',
                values: '—',
                default: 'chevron-right'
            },
            {
                name: 'defaultLocale',
                description: `Will be used for default date and number formatting. Accept a string with a BCP 47 language tag, or an array of such strings.
                See Unicode BCP 47 locale identifier`,
                type: 'String, Array of String',
                values: '—',
                default: 'undefined: default to browser locale.'
            },
            {
                name: 'defaultContainerElement</>',
                description: `Default container attribute for floating Notices (Toasts & Snackbars). Note that this also
                    changes the position of the Notices from fixed to absolute.
                    Meaning that the container should be fixed.`,
                type: 'String',
                values: '—',
                default: 'body'
            },
            {
                name: 'defaultDialogConfirmText',
                description: `Default dialog <code>confirmText</code> attribute`,
                type: 'String',
                values: '—',
                default: 'OK'
            },
            {
                name: 'defaultDialogCancelText',
                description: `Default dialog <code>cancelText</code> attribute`,
                type: 'String',
                values: '—',
                default: 'Cancel'
            },
            {
                name: 'defaultSnackbarDuration',
                description: `Default snackbar <code>duration</code> attribute.`,
                type: 'Number',
                values: '—',
                default: '3500'
            },
            {
                name: 'defaultSnackbarPosition',
                description: `Default snackbar <code>position</code> attribute.`,
                type: 'String',
                values: `is-top-right, is-top, is-top-left,
                    is-bottom-right, is-bottom, is-bottom-left`,
                default: 'is-bottom-right'
            },
            {
                name: 'defaultToastDuration',
                description: `Default toast <code>duration</code> attribute.`,
                type: 'Number',
                values: '—',
                default: '2000'
            },
            {
                name: 'defaultToastPosition',
                description: `Default toast <code>position</code> attribute.`,
                type: 'String',
                values: `is-top-right, is-top, is-top-left,
                    is-bottom-right, is-bottom, is-bottom-left`,
                default: 'is-top'
            },
            {
                name: 'defaultNotificationDuration',
                description: `Default notification <code>duration</code> attribute.`,
                type: 'Number',
                values: '—',
                default: '2000'
            },
            {
                name: 'defaultNotificationPosition',
                description: `Default notification <code>position</code> attribute for programmatically opening.`,
                type: 'String',
                values: `is-top-right, is-top, is-top-left,
                    is-bottom-right, is-bottom, is-bottom-left`,
                default: 'is-bottom-right'
            },
            {
                name: 'defaultTooltipType',
                description: `Default tooltip type (color) attribute.`,
                type: 'String',
                values: `is-white, is-black, is-light,
                    is-dark, is-primary, is-info, is-success,
                    is-warning, is-danger,
                    and any other colors you've set in the $colors list on Sass`,
                default: 'is-primary'
            },
            {
                name: 'defaultTooltipDelay',
                description: `Default tooltip delay attribute.`,
                type: 'Number',
                values: `—`,
                default: '—'
            },
            {
                name: 'defaultInputAutocomplete',
                description: `Default input <code>autocomplete</code> attribute.`,
                type: 'String',
                values: 'on, off',
                default: 'on'
            },
            {
                name: 'defaultDateFormatter',
                description: `Default datepicker <code>date-formatter</code> attribute`,
                type: 'Function',
                values: '—',
                default: '(date) => new Intl.DateTimeFormat(locale, { timezome: "UTC" }).format(date)'
            },
            {
                name: 'defaultDateParser',
                description: `Default datepicker <code>date-parser</code> attribute`,
                type: 'Function',
                values: '—',
                default: 'Tries to parse the date using the locale specific format. Fallback to Date.parse'
            },
            {
                name: 'defaultDateCreator',
                description: `Default datepicker <code>date-creator</code> attribute`,
                type: 'Function',
                values: '—',
                default: 'new Date()'
            },
            {
                name: 'defaultDayNames',
                description: `Default datepicker <code>day-names</code> attribute`,
                type: 'Array',
                values: '—',
                default: `default to browser locale`
            },
            {
                name: 'defaultMonthNames',
                description: `Default datepicker <code>month-names</code> attribute`,
                type: 'Array',
                values: '—',
                default: `default to browser locale`
            },
            {
                name: 'defaultFirstDayOfWeek',
                description: `Default datepicker <code>first-day-of-week</code> attribute`,
                type: 'Number',
                values: '—',
                default: '0'
            },
            {
                name: 'defaultUnselectableDaysOfWeek',
                description: `Default datepicker <code>unselectable-days-of-week</code> attribute`,
                type: 'Array',
                values: '—',
                default: '—'
            },
            {
                name: 'defaultTimeFormatter',
                description: `Default timepicker <code>time-formatter</code> attribute`,
                type: 'Function',
                values: '—',
                default: 'HH:mm or HH:mm AM/PM'
            },
            {
                name: 'defaultTimeParser',
                description: `Default timepicker <code>time-parser</code> attribute`,
                type: 'Function',
                values: '—',
                default: 'HH:mm or HH:mm AM/PM'
            },
            {
                name: 'defaultModalCanCancel',
                description: `Default modal <code>canCancel</code> attribute`,
                type: 'Boolean, Array',
                values: 'escape, x, outside, button',
                default: `<code>['escape', 'x', 'outside', 'button']</code>`
            },
            {
                name: 'defaultModalScroll',
                description: `Default modal/dialog <code>scroll</code> attribute`,
                type: 'String',
                values: 'clip, keep',
                default: 'clip'
            },
            {
                name: 'defaultDatepickerMobileNative',
                description: 'Default native datepicker on mobile',
                type: 'Boolean',
                values: '—',
                default: 'true'
            },
            {
                name: 'defaultTimepickerMobileNative',
                description: 'Default native timepicker on mobile',
                type: 'Boolean',
                values: '—',
                default: 'true'
            },
            {
                name: 'defaultNoticeQueue',
                description: 'Default snackbar/toast/notification queue attribute',
                type: 'Boolean',
                values: '—',
                default: 'true'
            },
            {
                name: 'defaultInputHasCounter',
                description: 'Default input has-counter attribute',
                type: 'Boolean',
                values: '—',
                default: 'true'
            },
            {
                name: 'defaultTaginputHasCounter',
                description: 'Default taginput has-counter attribute',
                type: 'Boolean',
                values: '—',
                default: 'true'
            },
            {
                name: 'defaultUseHtml5Validation',
                description: 'Default form components use-html5-validation attribute',
                type: 'Boolean',
                values: '—',
                default: 'true'
            },
            {
                name: 'defaultFieldLabelPosition',
                description: 'Default field position of label',
                type: 'String',
                values: 'inside,on-border',
                default: '—'
            },
            {
                name: 'defaultDatepickerYearsRange',
                description: 'Default years range relative to selected year',
                type: 'Array',
                values: '—',
                default: '[-100, 3]'
            },
            {
                name: 'defaultDatepickerNearbyMonthDays',
                description: 'Show/Hide nearby month days (prev and next month)',
                type: 'Boolean',
                values: '—',
                default: 'true'
            },
            {
                name: 'defaultDatepickerNearbySelectableMonthDays',
                description: 'It allows to select/unselect nearby month days',
                type: 'Boolean',
                values: '—',
                default: 'false'
            },
            {
                name: 'defaultDatepickerShowWeekNumber',
                description: 'It allows to display week number',
                type: 'Boolean',
                values: '—',
                default: 'false'
            },
            {
                name: 'customIconPacks',
                description: 'Allows you to define your own custom icon pack to be used in Buefy.',
                type: 'Object',
                values: '—',
                default: '—'
            },
            {
                name: 'defaultClockpickerHoursLabel',
                description: `Default clockpicker <code>hours-label</code> attribute`,
                type: 'String',
                values: '—',
                default: `<code>Hours</code>`
            },
            {
                name: 'defaultClockpickerMinutesLabel',
                description: `Default clockpicker <code>minutes-label</code> attribute`,
                type: 'String',
                values: '—',
                default: `<code>Min</code>`
            },
            {
                name: 'defaultTrapFocus',
                description: 'Default config to trap focus inside components (Dialog, Dropdown and Modal).',
                type: 'Boolean',
                values: '—',
                default: 'true'
            },
            {
                name: 'defaultButtonRounded',
                description: 'Default config to make all buttons rounded.',
                type: 'Boolean',
                values: '—',
                default: 'false'
            },
            {
                name: 'defaultCarouselInterval',
                description: 'Default carousel interval attribute',
                type: 'Number',
                values: '—',
                default: '3500'
            },
            {
                name: 'defaultTabsExpanded',
                description: 'Default tabs expanded attribute',
                type: 'Boolean',
                values: '—',
                default: 'false'
            },
            {
                name: 'defaultTabsAnimated',
                description: 'Default tabs animated attribute',
                type: 'Boolean',
                values: '—',
                default: 'true'
            },
            {
                name: 'defaultTabsType',
                description: 'Default tabs type attribute',
                type: 'String',
                values: 'is-boxed, is-toggle',
                default: '—'
            },
            {
                name: 'defaultProgrammaticPromise',
                description: `Return a promise using programmatic component`,
                type: 'Boolean',
                values: '—',
                default: 'false'
            },
            {
                name: 'defaultLinkTags',
                description: 'Default link tags accepted as tag in some component (Button, MenuItem, PaginationButton). Can be used when using custom component or other Vue Router plugin like inertia.',
                type: 'string',
                values: '—',
                default: '[a, button, input, router-link, nuxt-link, n-link, RouterLink, NuxtLink, NLink]'
            },
            {
                name: 'defaultImageWebpFallback',
                description: `Fallback when using webp format. You can specify an extension only (.jpg, .jpeg, .png, .gif) if the filename is the same. You can use a full url if not.`,
                type: 'String',
                values: '—',
                default: '—'
            },
            {
                name: 'defaultImageLazy',
                description: `Use IntersectionObserver to display the image only when in viewport.`,
                type: 'Boolean',
                values: '—',
                default: 'true'
            },
            {
                name: 'defaultImageResponsive',
                description: `The image will take 100% of the parent width. Use this with <code>ratio</code> to prevent page jump when images are loading.`,
                type: 'Boolean',
                values: '—',
                default: 'true'
            },
            {
                name: 'defaultImageRatio',
                description: `The space will be reserved. Prevent page jump when images are loading when using <code>responsive</code>.`,
                type: 'String',
                values: '1by1, 5by4, 4by3, 3by2, 5by3, 16by9, 2by1, 3by1, 4by5, 3by4, 2by3, 3by5, 9by16, 1by2 1by3 or any string having this format {number}by{number}',
                default: '—'
            },
            {
                name: 'defaultImageSrcsetFormatter',
                description: `Function to format src according to a given size.`,
                type: 'Function',
                values: '—',
                default: '(src, size) => `${srcFilename}-${size}.${srcExt}`'
            }
        ]
    }
]
<|endoftext|>


<commit_before>(function(angular, factory) {
    'use strict';
    if (typeof define === 'function' && define.amd) {
        define('ngStorage', ['angular'], function(angular) {
            return factory(angular);
        });
    } else {
        return factory(angular);
    }
}(typeof angular === 'undefined' ? null : angular, function(angular) {

    'use strict';

    /**
     * @ngdoc overview
     * @name ngStorage
     */

    angular.module('ngStorage', [])

    /**
     * @ngdoc object
     * @name ngStorage.$localStorage
     * @requires $rootScope
     * @requires $window
     */

    .factory('$localStorage', _storageFactory('localStorage'))

    /**
     * @ngdoc object
     * @name ngStorage.$sessionStorage
     * @requires $rootScope
     * @requires $window
     */

    .factory('$sessionStorage', _storageFactory('sessionStorage'));

    function _storageFactory(storageType) {
        return [
            '$rootScope',
            '$window',
            '$log',
            '$timeout',

            function(
                $rootScope,
                $window,
                $log,
                $timeout
            ){
                function isStorageSupported(storageType) {

                    // fix 'SecurityError: DOM Exception 18' exception in Desktop Safari, Mobile Safari
                    // when "Block cookies": "Always block" is turned on
                    var supported;
                    try {
                        supported = $window[storageType];
                    }
                    catch (err) {
                        supported = false;
                    }

                    // When Safari (OS X or iOS) is in private browsing mode, it appears as though localStorage
                    // is available, but trying to call .setItem throws an exception below:
                    // "QUOTA_EXCEEDED_ERR: DOM Exception 22: An attempt was made to add something to storage that exceeded the quota."
                    if (supported && storageType === 'localStorage') {
                        var key = '__' + Math.round(Math.random() * 1e7);

                        try {
                            localStorage.setItem(key, key);
                            localStorage.removeItem(key);
                        }
                        catch (err) {
                            supported = false;
                        }
                    }

                    return supported;
                }

                // #9: Assign a placeholder object if Web Storage is unavailable to prevent breaking the entire AngularJS app
                var webStorage = isStorageSupported(storageType) || ($log.warn('This browser does not support Web Storage!'), {setItem: function() {}, getItem: function() {}}),
                    $storage = {
                        $default: function(items) {
                            for (var k in items) {
                                angular.isDefined($storage[k]) || ($storage[k] = items[k]);
                            }

                            return $storage;
                        },
                        $reset: function(items) {
                            for (var k in $storage) {
                                '$' === k[0] || (delete $storage[k] && webStorage.removeItem('ngStorage-' + k));
                            }

                            return $storage.$default(items);
                        }
                    },
                    _last$storage,
                    _debounce;

                try {
                    webStorage = $window[storageType];
                    webStorage.length;
                } catch(e) {
                    $log.warn('This browser does not support Web Storage!');
                    webStorage = {};
                }

                for (var i = 0, l = webStorage.length, k; i < l; i++) {
                    // #8, #10: `webStorage.key(i)` may be an empty string (or throw an exception in IE9 if `webStorage` is empty)
                    (k = webStorage.key(i)) && 'ngStorage-' === k.slice(0, 10) && ($storage[k.slice(10)] = angular.fromJson(webStorage.getItem(k)));
                }

                _last$storage = angular.copy($storage);

                $rootScope.$watch(function() {
                    var temp$storage;
                    _debounce || (_debounce = $timeout(function() {
                        _debounce = null;

                        if (!angular.equals($storage, _last$storage)) {
                            temp$storage = angular.copy(_last$storage);
                            angular.forEach($storage, function(v, k) {
                                angular.isDefined(v) && '$' !== k[0] && webStorage.setItem('ngStorage-' + k, angular.toJson(v));

                                delete temp$storage[k];
                            });

                            for (var k in temp$storage) {
                                webStorage.removeItem('ngStorage-' + k);
                            }

                            _last$storage = angular.copy($storage);
                        }
                    }, 100, false));
                });

                // #6: Use `$window.addEventListener` instead of `angular.element` to avoid the jQuery-specific `event.originalEvent`
                'localStorage' === storageType && $window.addEventListener && $window.addEventListener('storage', function(event) {
                    if ('ngStorage-' === event.key.slice(0, 10)) {
                        event.newValue ? $storage[event.key.slice(10)] = angular.fromJson(event.newValue) : delete $storage[event.key.slice(10)];

                        _last$storage = angular.copy($storage);

                        $rootScope.$apply();
                    }
                });

                return $storage;
            }
        ];
    }

}));
<commit_msg>Add comment by @bmulholland for issue #75.<commit_after>(function(angular, factory) {
    'use strict';
    if (typeof define === 'function' && define.amd) {
        define('ngStorage', ['angular'], function(angular) {
            return factory(angular);
        });
    } else {
        return factory(angular);
    }
}(typeof angular === 'undefined' ? null : angular, function(angular) {

    'use strict';

    /**
     * @ngdoc overview
     * @name ngStorage
     */

    angular.module('ngStorage', [])

    /**
     * @ngdoc object
     * @name ngStorage.$localStorage
     * @requires $rootScope
     * @requires $window
     */

    .factory('$localStorage', _storageFactory('localStorage'))

    /**
     * @ngdoc object
     * @name ngStorage.$sessionStorage
     * @requires $rootScope
     * @requires $window
     */

    .factory('$sessionStorage', _storageFactory('sessionStorage'));

    function _storageFactory(storageType) {
        return [
            '$rootScope',
            '$window',
            '$log',
            '$timeout',

            function(
                $rootScope,
                $window,
                $log,
                $timeout
            ){
                function isStorageSupported(storageType) {

                    // Some installations of IE, for an unknown reason, throw "SCRIPT5: Error: Access is denied"
                    // when accessing window.localStorage. This happens before you try to do anything with it. Catch
                    // that error and allow execution to continue.

                    // fix 'SecurityError: DOM Exception 18' exception in Desktop Safari, Mobile Safari
                    // when "Block cookies": "Always block" is turned on
                    var supported;
                    try {
                        supported = $window[storageType];
                    }
                    catch (err) {
                        supported = false;
                    }

                    // When Safari (OS X or iOS) is in private browsing mode, it appears as though localStorage
                    // is available, but trying to call .setItem throws an exception below:
                    // "QUOTA_EXCEEDED_ERR: DOM Exception 22: An attempt was made to add something to storage that exceeded the quota."
                    if (supported && storageType === 'localStorage') {
                        var key = '__' + Math.round(Math.random() * 1e7);

                        try {
                            localStorage.setItem(key, key);
                            localStorage.removeItem(key);
                        }
                        catch (err) {
                            supported = false;
                        }
                    }

                    return supported;
                }

                // #9: Assign a placeholder object if Web Storage is unavailable to prevent breaking the entire AngularJS app
                var webStorage = isStorageSupported(storageType) || ($log.warn('This browser does not support Web Storage!'), {setItem: function() {}, getItem: function() {}}),
                    $storage = {
                        $default: function(items) {
                            for (var k in items) {
                                angular.isDefined($storage[k]) || ($storage[k] = items[k]);
                            }

                            return $storage;
                        },
                        $reset: function(items) {
                            for (var k in $storage) {
                                '$' === k[0] || (delete $storage[k] && webStorage.removeItem('ngStorage-' + k));
                            }

                            return $storage.$default(items);
                        }
                    },
                    _last$storage,
                    _debounce;

                try {
                    webStorage = $window[storageType];
                    webStorage.length;
                } catch(e) {
                    $log.warn('This browser does not support Web Storage!');
                    webStorage = {};
                }

                for (var i = 0, l = webStorage.length, k; i < l; i++) {
                    // #8, #10: `webStorage.key(i)` may be an empty string (or throw an exception in IE9 if `webStorage` is empty)
                    (k = webStorage.key(i)) && 'ngStorage-' === k.slice(0, 10) && ($storage[k.slice(10)] = angular.fromJson(webStorage.getItem(k)));
                }

                _last$storage = angular.copy($storage);

                $rootScope.$watch(function() {
                    var temp$storage;
                    _debounce || (_debounce = $timeout(function() {
                        _debounce = null;

                        if (!angular.equals($storage, _last$storage)) {
                            temp$storage = angular.copy(_last$storage);
                            angular.forEach($storage, function(v, k) {
                                angular.isDefined(v) && '$' !== k[0] && webStorage.setItem('ngStorage-' + k, angular.toJson(v));

                                delete temp$storage[k];
                            });

                            for (var k in temp$storage) {
                                webStorage.removeItem('ngStorage-' + k);
                            }

                            _last$storage = angular.copy($storage);
                        }
                    }, 100, false));
                });

                // #6: Use `$window.addEventListener` instead of `angular.element` to avoid the jQuery-specific `event.originalEvent`
                'localStorage' === storageType && $window.addEventListener && $window.addEventListener('storage', function(event) {
                    if ('ngStorage-' === event.key.slice(0, 10)) {
                        event.newValue ? $storage[event.key.slice(10)] = angular.fromJson(event.newValue) : delete $storage[event.key.slice(10)];

                        _last$storage = angular.copy($storage);

                        $rootScope.$apply();
                    }
                });

                return $storage;
            }
        ];
    }

}));
<|endoftext|>


<commit_before>import { module, test } from 'qunit';
import EmberObject, { computed } from '@ember/object';
import EmberMixin from '@ember/object/mixin';
import { dasherize } from '@ember/string';

import bindDataTestAttributes from 'ember-test-selectors/utils/bind-data-test-attributes';

module('Unit | Utility | bind data test attributes');

test('it adds missing attributeBindings array', function(assert) {
  let Fixture = EmberObject.extend({
    'data-test-from-factory': 'foo',
  });
  let instance = Fixture.create({
    'data-test-from-invocation': 'bar',
  });

  assert.deepEqual(instance.get('attributeBindings'), undefined);

  bindDataTestAttributes(instance);

  assert.deepEqual(instance.get('attributeBindings'),
    ['data-test-from-invocation', 'data-test-from-factory']);
});

test('it adds to existing attributeBindings array', function(assert) {
  let Fixture = EmberObject.extend({
    attributeBindings: ['foo', 'bar'],

    foo: 1,
    bar: 2,

    'data-test-from-factory': 'foo',
  });
  let instance = Fixture.create({
    'data-test-from-invocation': 'bar',
  });

  assert.deepEqual(instance.get('attributeBindings'), ['foo', 'bar']);

  bindDataTestAttributes(instance);

  assert.deepEqual(instance.get('attributeBindings'),
    ['foo', 'bar', 'data-test-from-invocation', 'data-test-from-factory']);
});

test('it converts existing attributeBindings string to array', function(assert) {
  let Fixture = EmberObject.extend({
    attributeBindings: 'foo',

    foo: 1,

    'data-test-from-factory': 'foo',
  });
  let instance = Fixture.create({
    'data-test-from-invocation': 'bar',
  });

  assert.deepEqual(instance.get('attributeBindings'), 'foo');

  bindDataTestAttributes(instance);

  assert.deepEqual(instance.get('attributeBindings'),
    ['foo', 'data-test-from-invocation', 'data-test-from-factory']);
});

test('it only adds data-test-* properties', function(assert) {
  let Fixture = EmberObject.extend({
    foo: 1,
    bar: 2,

    'data-test-from-factory': 'foo',
  });
  let instance = Fixture.create({
    baz: 3,

    'data-test-from-invocation': 'bar',
  });

  assert.deepEqual(instance.get('attributeBindings'), undefined);

  bindDataTestAttributes(instance);

  assert.deepEqual(instance.get('attributeBindings'),
    ['data-test-from-invocation', 'data-test-from-factory']);
});

test('it does not add a data-test property', function(assert) {
  let Fixture = EmberObject.extend({
    'data-test': 'foo',
  });
  let instance = Fixture.create();

  assert.deepEqual(instance.get('attributeBindings'), undefined);

  bindDataTestAttributes(instance);

  assert.deepEqual(instance.get('attributeBindings'), undefined);
});

test('it breaks if attributeBindings is a computed property', function(assert) {
  let Fixture = EmberObject.extend({
    attributeBindings: computed('prop', function() {
      return [this.get('prop')];
    }).readOnly(),

    foo: 5,

    'data-test-from-factory': 'foo',
  });
  let instance = Fixture.create({
    prop: 'foo',

    'data-test-from-invocation': 'bar',
  });

  assert.throws(() => bindDataTestAttributes(instance));
});

test('it breaks if tagName is empty', function(assert) {
  let Fixture = EmberObject.extend({
    tagName: '',
    'data-test-from-factory': 'foo',
  });
  let instance = Fixture.create({
    'data-test-from-invocation': 'bar',
  });

  assert.throws(() => bindDataTestAttributes(instance));
});

test('it does not breaks if tagName is empty and supportsDataTestProperties is set', function(assert) {
  assert.expect(0);

  let Fixture = EmberObject.extend({
    tagName: '',
    supportsDataTestProperties: true,
    'data-test-from-factory': 'foo',
  });
  let instance = Fixture.create({
    'data-test-from-invocation': 'bar',
  });

  bindDataTestAttributes(instance);
});

test('issue #106', function(assert) {
  let Component = EmberObject.extend({});

  Component.reopen({
    init() {
      this._super(...arguments);
      bindDataTestAttributes(this);
    },
  });

  let Mixin = EmberMixin.create({
    init() {
      this._super(...arguments);

      if (this.tagName !== '') {
        let componentName = dasherize(this._XXXdebugContainerKey.replace(/\//g, '-').split(':')[1]);
        this.set('data-test-component', componentName);

        let dataTestAttr = ['data-test-component'];
        this.attributeBindings = this.attributeBindings ? this.attributeBindings.concat(dataTestAttr) : dataTestAttr;
      }
    },
  });

  Component.reopen(Mixin);

  let Fixture1 = Component.extend({
    _XXXdebugContainerKey: 'component:fixture1',
    tagName: 'span',
  });

  let fixture1 = Fixture1.create();

  assert.strictEqual(fixture1.get('data-test-component'), 'fixture1');

  let Fixture2 = Component.extend({
    _XXXdebugContainerKey: 'component:fixture2',
    tagName: '',
  });

  let fixture2 = Fixture2.create();

  assert.strictEqual(fixture2.get('data-test-component'), undefined);
});

<commit_msg>tests/utils: Use nested test suite syntax<commit_after>import { module, test } from 'qunit';
import EmberObject, { computed } from '@ember/object';
import EmberMixin from '@ember/object/mixin';
import { dasherize } from '@ember/string';

import bindDataTestAttributes from 'ember-test-selectors/utils/bind-data-test-attributes';

module('Unit | Utility | bind data test attributes', function() {
  test('it adds missing attributeBindings array', function(assert) {
    let Fixture = EmberObject.extend({
      'data-test-from-factory': 'foo',
    });
    let instance = Fixture.create({
      'data-test-from-invocation': 'bar',
    });

    assert.deepEqual(instance.get('attributeBindings'), undefined);

    bindDataTestAttributes(instance);

    assert.deepEqual(instance.get('attributeBindings'),
      ['data-test-from-invocation', 'data-test-from-factory']);
  });

  test('it adds to existing attributeBindings array', function(assert) {
    let Fixture = EmberObject.extend({
      attributeBindings: ['foo', 'bar'],

      foo: 1,
      bar: 2,

      'data-test-from-factory': 'foo',
    });
    let instance = Fixture.create({
      'data-test-from-invocation': 'bar',
    });

    assert.deepEqual(instance.get('attributeBindings'), ['foo', 'bar']);

    bindDataTestAttributes(instance);

    assert.deepEqual(instance.get('attributeBindings'),
      ['foo', 'bar', 'data-test-from-invocation', 'data-test-from-factory']);
  });

  test('it converts existing attributeBindings string to array', function(assert) {
    let Fixture = EmberObject.extend({
      attributeBindings: 'foo',

      foo: 1,

      'data-test-from-factory': 'foo',
    });
    let instance = Fixture.create({
      'data-test-from-invocation': 'bar',
    });

    assert.deepEqual(instance.get('attributeBindings'), 'foo');

    bindDataTestAttributes(instance);

    assert.deepEqual(instance.get('attributeBindings'),
      ['foo', 'data-test-from-invocation', 'data-test-from-factory']);
  });

  test('it only adds data-test-* properties', function(assert) {
    let Fixture = EmberObject.extend({
      foo: 1,
      bar: 2,

      'data-test-from-factory': 'foo',
    });
    let instance = Fixture.create({
      baz: 3,

      'data-test-from-invocation': 'bar',
    });

    assert.deepEqual(instance.get('attributeBindings'), undefined);

    bindDataTestAttributes(instance);

    assert.deepEqual(instance.get('attributeBindings'),
      ['data-test-from-invocation', 'data-test-from-factory']);
  });

  test('it does not add a data-test property', function(assert) {
    let Fixture = EmberObject.extend({
      'data-test': 'foo',
    });
    let instance = Fixture.create();

    assert.deepEqual(instance.get('attributeBindings'), undefined);

    bindDataTestAttributes(instance);

    assert.deepEqual(instance.get('attributeBindings'), undefined);
  });

  test('it breaks if attributeBindings is a computed property', function(assert) {
    let Fixture = EmberObject.extend({
      attributeBindings: computed('prop', function() {
        return [this.get('prop')];
      }).readOnly(),

      foo: 5,

      'data-test-from-factory': 'foo',
    });
    let instance = Fixture.create({
      prop: 'foo',

      'data-test-from-invocation': 'bar',
    });

    assert.throws(() => bindDataTestAttributes(instance));
  });

  test('it breaks if tagName is empty', function(assert) {
    let Fixture = EmberObject.extend({
      tagName: '',
      'data-test-from-factory': 'foo',
    });
    let instance = Fixture.create({
      'data-test-from-invocation': 'bar',
    });

    assert.throws(() => bindDataTestAttributes(instance));
  });

  test('it does not breaks if tagName is empty and supportsDataTestProperties is set', function(assert) {
    assert.expect(0);

    let Fixture = EmberObject.extend({
      tagName: '',
      supportsDataTestProperties: true,
      'data-test-from-factory': 'foo',
    });
    let instance = Fixture.create({
      'data-test-from-invocation': 'bar',
    });

    bindDataTestAttributes(instance);
  });

  test('issue #106', function(assert) {
    let Component = EmberObject.extend({});

    Component.reopen({
      init() {
        this._super(...arguments);
        bindDataTestAttributes(this);
      },
    });

    let Mixin = EmberMixin.create({
      init() {
        this._super(...arguments);

        if (this.tagName !== '') {
          let componentName = dasherize(this._XXXdebugContainerKey.replace(/\//g, '-').split(':')[1]);
          this.set('data-test-component', componentName);

          let dataTestAttr = ['data-test-component'];
          this.attributeBindings = this.attributeBindings ? this.attributeBindings.concat(dataTestAttr) : dataTestAttr;
        }
      },
    });

    Component.reopen(Mixin);

    let Fixture1 = Component.extend({
      _XXXdebugContainerKey: 'component:fixture1',
      tagName: 'span',
    });

    let fixture1 = Fixture1.create();

    assert.strictEqual(fixture1.get('data-test-component'), 'fixture1');

    let Fixture2 = Component.extend({
      _XXXdebugContainerKey: 'component:fixture2',
      tagName: '',
    });

    let fixture2 = Fixture2.create();

    assert.strictEqual(fixture2.get('data-test-component'), undefined);
  });
});
<|endoftext|>


<commit_before>import xhr from 'xhr';
import _ from 'lodash';
import Logger from './Logger';

let log = Logger.namespace('ConfigLoader');

export default class ConfigLoader {
    constructor() {
        this.config = Object.create(null);
    }

    loadFromUrl(configUrl) {
        return new Promise((resolve, reject) => {
            xhr({ url: configUrl }, (err, response) => {
                if (err) {
                    reject();
                    return;
                }

                let configObj = null;
                try {
                    configObj = JSON.parse(response.body);
                } catch (parseErr) {
                    log.error('Config file: ' + parseErr.stack);
                    reject();
                    return;
                }

                this.setConfig(configObj);
                resolve(this.config);
            });
        });
    }

    loadFromObj(configObj) {
        return new Promise((resolve, reject) => {
            this.setConfig(configObj);
            resolve(this.config);
        });
    }

    setConfig(confObj) {
        this.config = Object.create(null);
        _.each(confObj, (_val, key) => {
            let val = _val;
            if (typeof val === 'string') {
                val = this.insertReplacements(val);
            }

            this.config[key] = val;
        });
    }

    insertReplacements(input) {
        let out = input;
        out = out.replace('{{hostname}}', window.location.hostname);
        out = out.replace('{{host}}', window.location.host);
        out = out.replace('{{port}}', window.location.port || 80);
        out = out.replace('{{hash}}', (window.location.hash || '').substr(1));
        out = out.replace('{{query}}', (window.location.search || '').substr(1));
        out = out.replace('{{referrer}}', window.document.referrer);
        return out;
    }
}
<commit_msg>better log output for config errors<commit_after>import xhr from 'xhr';
import _ from 'lodash';
import Logger from './Logger';

let log = Logger.namespace('ConfigLoader');

export default class ConfigLoader {
    constructor() {
        this.config = Object.create(null);
    }

    loadFromUrl(configUrl) {
        return new Promise((resolve, reject) => {
            xhr({ url: configUrl }, (err, response) => {
                if (err) {
                    reject();
                    return;
                }

                let configObj = null;
                try {
                    configObj = JSON.parse(response.body);
                } catch (parseErr) {
                    log.error('Config ' + parseErr.message);
                    reject();
                    return;
                }

                this.setConfig(configObj);
                resolve(this.config);
            });
        });
    }

    loadFromObj(configObj) {
        return new Promise((resolve, reject) => {
            this.setConfig(configObj);
            resolve(this.config);
        });
    }

    setConfig(confObj) {
        this.config = Object.create(null);
        _.each(confObj, (_val, key) => {
            let val = _val;
            if (typeof val === 'string') {
                val = this.insertReplacements(val);
            }

            this.config[key] = val;
        });
    }

    insertReplacements(input) {
        let out = input;
        out = out.replace('{{hostname}}', window.location.hostname);
        out = out.replace('{{host}}', window.location.host);
        out = out.replace('{{port}}', window.location.port || 80);
        out = out.replace('{{hash}}', (window.location.hash || '').substr(1));
        out = out.replace('{{query}}', (window.location.search || '').substr(1));
        out = out.replace('{{referrer}}', window.document.referrer);
        return out;
    }
}
<|endoftext|>


<commit_before>(function (root, factory) {
  'use strict';

  if (typeof define === 'function' && define.amd) {
    define(['angular'], factory);
  } else if (typeof exports === 'object') {
    module.exports = factory(require('angular'));
  } else {
    // Browser globals (root is window), we don't register it.
    factory(root.angular);
  }
}(this , function (angular) {
    'use strict';

    // RequireJS does not pass in Angular to us (will be undefined).
    // Fallback to window which should mostly be there.
    angular = (angular && angular.module ) ? angular : window.angular;

    /**
     * @ngdoc overview
     * @name ngStorage
     */

    return angular.module('ngStorage', [])

    /**
     * @ngdoc object
     * @name ngStorage.$localStorage
     * @requires $rootScope
     * @requires $window
     */

    .provider('$localStorage', _storageProvider('localStorage'))

    /**
     * @ngdoc object
     * @name ngStorage.$sessionStorage
     * @requires $rootScope
     * @requires $window
     */

    .provider('$sessionStorage', _storageProvider('sessionStorage'));

    function _storageProvider(storageType) {
        return function () {
          var storageKeyPrefix = 'ngStorage-';

          this.setKeyPrefix = function (prefix) {
            if (typeof prefix !== 'string') {
              throw new TypeError('[ngStorage] - ' + storageType + 'Provider.setKeyPrefix() expects a String.');
            }
            storageKeyPrefix = prefix;
          };

          var serializer = angular.toJson;
          var deserializer = angular.fromJson;

          this.setSerializer = function (s) {
            if (typeof s !== 'function') {
              throw new TypeError('[ngStorage] - ' + storageType + 'Provider.setSerializer expects a function.');
            }

            serializer = s;
          };

          this.setDeserializer = function (d) {
            if (typeof d !== 'function') {
              throw new TypeError('[ngStorage] - ' + storageType + 'Provider.setDeserializer expects a function.');
            }

            deserializer = d;
          };

          // Note: This is not very elegant at all.
          this.get = function (key) {
            return deserializer(window[storageType].getItem(storageKeyPrefix + key));
          };

          // Note: This is not very elegant at all.
          this.set = function (key, value) {
            return window[storageType].setItem(storageKeyPrefix + key, serializer(value));
          };

          this.$get = [
              '$rootScope',
              '$window',
              '$log',
              '$timeout',

              function(
                  $rootScope,
                  $window,
                  $log,
                  $timeout
              ){
                function isStorageSupported(storageType) {

                    // Some installations of IE, for an unknown reason, throw "SCRIPT5: Error: Access is denied"
                    // when accessing window.localStorage. This happens before you try to do anything with it. Catch
                    // that error and allow execution to continue.

                    // fix 'SecurityError: DOM Exception 18' exception in Desktop Safari, Mobile Safari
                    // when "Block cookies": "Always block" is turned on
                    var supported;
                    try {
                        supported = $window[storageType];
                    }
                    catch (err) {
                        supported = false;
                    }

                    // When Safari (OS X or iOS) is in private browsing mode, it appears as though localStorage
                    // is available, but trying to call .setItem throws an exception below:
                    // "QUOTA_EXCEEDED_ERR: DOM Exception 22: An attempt was made to add something to storage that exceeded the quota."
                    if (supported && storageType === 'localStorage') {
                        var key = '__' + Math.round(Math.random() * 1e7);

                        try {
                            localStorage.setItem(key, key);
                            localStorage.removeItem(key);
                        }
                        catch (err) {
                            supported = false;
                        }
                    }

                    return supported;
                }

                // The magic number 10 is used which only works for some keyPrefixes...
                // See https://github.com/gsklee/ngStorage/issues/137
                var prefixLength = storageKeyPrefix.length;

                // #9: Assign a placeholder object if Web Storage is unavailable to prevent breaking the entire AngularJS app
                var webStorage = isStorageSupported(storageType) || ($log.warn('This browser does not support Web Storage!'), {setItem: angular.noop, getItem: angular.noop}),
                    $storage = {
                        $default: function(items) {
                            for (var k in items) {
                                angular.isDefined($storage[k]) || ($storage[k] = items[k]);
                            }

                            $storage.$sync();
                            return $storage;
                        },
                        $reset: function(items) {
                            for (var k in $storage) {
                                '$' === k[0] || (delete $storage[k] && webStorage.removeItem(storageKeyPrefix + k));
                            }

                            return $storage.$default(items);
                        },
                        $sync: function () {
                            for (var i = 0, l = webStorage.length, k; i < l; i++) {
                                // #8, #10: `webStorage.key(i)` may be an empty string (or throw an exception in IE9 if `webStorage` is empty)
                                (k = webStorage.key(i)) && storageKeyPrefix === k.slice(0, prefixLength) && ($storage[k.slice(prefixLength)] = deserializer(webStorage.getItem(k)));
                            }
                        },
                        $apply: function() {
                            var temp$storage;

                            _debounce = null;

                            if (!angular.equals($storage, _last$storage)) {
                                temp$storage = angular.copy(_last$storage);
                                angular.forEach($storage, function(v, k) {
                                    angular.isDefined(v) && '$' !== k[0] && webStorage.setItem(storageKeyPrefix + k, serializer(v));

                                    delete temp$storage[k];
                                });

                                for (var k in temp$storage) {
                                    webStorage.removeItem(storageKeyPrefix + k);
                                }

                                _last$storage = angular.copy($storage);
                            }
                        },
                    },
                    _last$storage,
                    _debounce;

                $storage.$sync();

                _last$storage = angular.copy($storage);

                $rootScope.$watch(function() {
                    _debounce || (_debounce = $timeout($storage.$apply, 100, false));
                });

                // #6: Use `$window.addEventListener` instead of `angular.element` to avoid the jQuery-specific `event.originalEvent`
                $window.addEventListener && $window.addEventListener('storage', function(event) {
                    if (storageKeyPrefix === event.key.slice(0, prefixLength)) {
                        event.newValue ? $storage[event.key.slice(prefixLength)] = deserializer(event.newValue) : delete $storage[event.key.slice(prefixLength)];

                        _last$storage = angular.copy($storage);

                        $rootScope.$apply();
                    }
                });

                $window.addEventListener && $window.addEventListener('beforeunload', function() {
                    $storage.$apply();
                });

                return $storage;
            }
        ];
      };
    }

}));
<commit_msg>Only delete when items is true for isDefined<commit_after>(function (root, factory) {
  'use strict';

  if (typeof define === 'function' && define.amd) {
    define(['angular'], factory);
  } else if (typeof exports === 'object') {
    module.exports = factory(require('angular'));
  } else {
    // Browser globals (root is window), we don't register it.
    factory(root.angular);
  }
}(this , function (angular) {
    'use strict';

    // RequireJS does not pass in Angular to us (will be undefined).
    // Fallback to window which should mostly be there.
    angular = (angular && angular.module ) ? angular : window.angular;

    /**
     * @ngdoc overview
     * @name ngStorage
     */

    return angular.module('ngStorage', [])

    /**
     * @ngdoc object
     * @name ngStorage.$localStorage
     * @requires $rootScope
     * @requires $window
     */

    .provider('$localStorage', _storageProvider('localStorage'))

    /**
     * @ngdoc object
     * @name ngStorage.$sessionStorage
     * @requires $rootScope
     * @requires $window
     */

    .provider('$sessionStorage', _storageProvider('sessionStorage'));

    function _storageProvider(storageType) {
        return function () {
          var storageKeyPrefix = 'ngStorage-';

          this.setKeyPrefix = function (prefix) {
            if (typeof prefix !== 'string') {
              throw new TypeError('[ngStorage] - ' + storageType + 'Provider.setKeyPrefix() expects a String.');
            }
            storageKeyPrefix = prefix;
          };

          var serializer = angular.toJson;
          var deserializer = angular.fromJson;

          this.setSerializer = function (s) {
            if (typeof s !== 'function') {
              throw new TypeError('[ngStorage] - ' + storageType + 'Provider.setSerializer expects a function.');
            }

            serializer = s;
          };

          this.setDeserializer = function (d) {
            if (typeof d !== 'function') {
              throw new TypeError('[ngStorage] - ' + storageType + 'Provider.setDeserializer expects a function.');
            }

            deserializer = d;
          };

          // Note: This is not very elegant at all.
          this.get = function (key) {
            return deserializer(window[storageType].getItem(storageKeyPrefix + key));
          };

          // Note: This is not very elegant at all.
          this.set = function (key, value) {
            return window[storageType].setItem(storageKeyPrefix + key, serializer(value));
          };

          this.$get = [
              '$rootScope',
              '$window',
              '$log',
              '$timeout',

              function(
                  $rootScope,
                  $window,
                  $log,
                  $timeout
              ){
                function isStorageSupported(storageType) {

                    // Some installations of IE, for an unknown reason, throw "SCRIPT5: Error: Access is denied"
                    // when accessing window.localStorage. This happens before you try to do anything with it. Catch
                    // that error and allow execution to continue.

                    // fix 'SecurityError: DOM Exception 18' exception in Desktop Safari, Mobile Safari
                    // when "Block cookies": "Always block" is turned on
                    var supported;
                    try {
                        supported = $window[storageType];
                    }
                    catch (err) {
                        supported = false;
                    }

                    // When Safari (OS X or iOS) is in private browsing mode, it appears as though localStorage
                    // is available, but trying to call .setItem throws an exception below:
                    // "QUOTA_EXCEEDED_ERR: DOM Exception 22: An attempt was made to add something to storage that exceeded the quota."
                    if (supported && storageType === 'localStorage') {
                        var key = '__' + Math.round(Math.random() * 1e7);

                        try {
                            localStorage.setItem(key, key);
                            localStorage.removeItem(key);
                        }
                        catch (err) {
                            supported = false;
                        }
                    }

                    return supported;
                }

                // The magic number 10 is used which only works for some keyPrefixes...
                // See https://github.com/gsklee/ngStorage/issues/137
                var prefixLength = storageKeyPrefix.length;

                // #9: Assign a placeholder object if Web Storage is unavailable to prevent breaking the entire AngularJS app
                var webStorage = isStorageSupported(storageType) || ($log.warn('This browser does not support Web Storage!'), {setItem: angular.noop, getItem: angular.noop}),
                    $storage = {
                        $default: function(items) {
                            for (var k in items) {
                                angular.isDefined($storage[k]) || ($storage[k] = items[k]);
                            }

                            $storage.$sync();
                            return $storage;
                        },
                        $reset: function(items) {
                            for (var k in $storage) {
                                '$' === k[0] || (delete $storage[k] && webStorage.removeItem(storageKeyPrefix + k));
                            }

                            return $storage.$default(items);
                        },
                        $sync: function () {
                            for (var i = 0, l = webStorage.length, k; i < l; i++) {
                                // #8, #10: `webStorage.key(i)` may be an empty string (or throw an exception in IE9 if `webStorage` is empty)
                                (k = webStorage.key(i)) && storageKeyPrefix === k.slice(0, prefixLength) && ($storage[k.slice(prefixLength)] = deserializer(webStorage.getItem(k)));
                            }
                        },
                        $apply: function() {
                            var temp$storage;

                            _debounce = null;

                            if (!angular.equals($storage, _last$storage)) {
                                temp$storage = angular.copy(_last$storage);
                                angular.forEach($storage, function(v, k) {
                                    if (angular.isDefined(v) && '$' !== k[0]) {
                                        webStorage.setItem(storageKeyPrefix + k, serializer(v))
                                        delete temp$storage[k];
                                    }
                                });

                                for (var k in temp$storage) {
                                    webStorage.removeItem(storageKeyPrefix + k);
                                }

                                _last$storage = angular.copy($storage);
                            }
                        },
                    },
                    _last$storage,
                    _debounce;

                $storage.$sync();

                _last$storage = angular.copy($storage);

                $rootScope.$watch(function() {
                    _debounce || (_debounce = $timeout($storage.$apply, 100, false));
                });

                // #6: Use `$window.addEventListener` instead of `angular.element` to avoid the jQuery-specific `event.originalEvent`
                $window.addEventListener && $window.addEventListener('storage', function(event) {
                    if (storageKeyPrefix === event.key.slice(0, prefixLength)) {
                        event.newValue ? $storage[event.key.slice(prefixLength)] = deserializer(event.newValue) : delete $storage[event.key.slice(prefixLength)];

                        _last$storage = angular.copy($storage);

                        $rootScope.$apply();
                    }
                });

                $window.addEventListener && $window.addEventListener('beforeunload', function() {
                    $storage.$apply();
                });

                return $storage;
            }
        ];
      };
    }

}));
<|endoftext|>


<commit_before>'use strict';

(function() {

    /**
     * @ngdoc overview
     * @name ngStorage
     */

    angular.module('ngStorage', []).

    /**
     * @ngdoc object
     * @name ngStorage.$localStorage
     * @requires $rootScope
     * @requires $browser
     * @requires $window
     */

    factory('$localStorage', _storageFactory('localStorage')).

    /**
     * @ngdoc object
     * @name ngStorage.$sessionStorage
     * @requires $rootScope
     * @requires $browser
     * @requires $window
     */

    factory('$sessionStorage', _storageFactory('sessionStorage'));

    function _storageFactory(storageType) {
        return [
            '$rootScope',
            '$browser',
            '$window',

            function(
                $rootScope,
                $browser,
                $window
            ){
                var webStorage = $window[storageType] || (console.warn('This browser does not support Web Storage!'), {}),

                    $storage = {
                        $default: function(items) {
                            for (var k in items) {
                                angular.isDefined($storage[k]) || ($storage[k] = items[k]);
                            }

                            return $storage;
                        },
                        $reset: function(items) {
                            for (var k in $storage) {
                                '$' === k[0] || delete $storage[k];
                            }

                            return $storage.$default(items);
                        }
                    },

                    _last$storage;

                // (#8) `i < webStorage.length` is needed for IE9
                for (var i = 0, k; i < webStorage.length; i++) {
                    if (k = webStorage.key(i)) {
                      'ngStorage-' === k.slice(0, 10) && ($storage[k.slice(10)] = angular.fromJson(webStorage.getItem(k)));
                    }
                }

                _last$storage = angular.copy($storage);

                $browser.addPollFn(function() {
                    if (!angular.equals($storage, _last$storage)) {
                        angular.forEach($storage, function(v, k) {
                            if (angular.isDefined(v) && '$' !== k[0]) {

                                // Remove $$hashKey and other things that cannot be stringified
                                $storage[k] = angular.fromJson(angular.toJson(v));

                                webStorage.setItem('ngStorage-' + k, angular.toJson(v));
                            }

                            delete _last$storage[k];
                        });

                        for (var k in _last$storage) {
                            webStorage.removeItem('ngStorage-' + k);
                        }

                        _last$storage = angular.copy($storage);

                        $rootScope.$apply();
                    }
                });

                // (#6) Use `$window.addEventListener` instead of `angular.element` to avoid the jQuery-specific `event.originalEvent`
                'localStorage' === storageType && $window.addEventListener('storage', function(event) {
                    if ('ngStorage-' === event.key.slice(0, 10)) {
                        event.newValue ? $storage[event.key.slice(10)] = angular.fromJson(event.newValue) : delete $storage[event.key.slice(10)];

                        _last$storage = angular.copy($storage);

                        $rootScope.$apply();
                    }
                });

                return $storage;
            }
        ];
    }

})();
<commit_msg>Code comment maintenance<commit_after>'use strict';

(function() {

    /**
     * @ngdoc overview
     * @name ngStorage
     */

    angular.module('ngStorage', []).

    /**
     * @ngdoc object
     * @name ngStorage.$localStorage
     * @requires $rootScope
     * @requires $browser
     * @requires $window
     */

    factory('$localStorage', _storageFactory('localStorage')).

    /**
     * @ngdoc object
     * @name ngStorage.$sessionStorage
     * @requires $rootScope
     * @requires $browser
     * @requires $window
     */

    factory('$sessionStorage', _storageFactory('sessionStorage'));

    function _storageFactory(storageType) {
        return [
            '$rootScope',
            '$browser',
            '$window',

            function(
                $rootScope,
                $browser,
                $window
            ){
                // #9: Assign a placeholder object if Web Storage is unavailable to prevent breaking the entire AngularJS app
                var webStorage = $window[storageType] || (console.warn('This browser does not support Web Storage!'), {}),

                    $storage = {
                        $default: function(items) {
                            for (var k in items) {
                                angular.isDefined($storage[k]) || ($storage[k] = items[k]);
                            }

                            return $storage;
                        },
                        $reset: function(items) {
                            for (var k in $storage) {
                                '$' === k[0] || delete $storage[k];
                            }

                            return $storage.$default(items);
                        }
                    },

                    _last$storage;

                // #8: `i < webStorage.length` is needed for IE9
                for (var i = 0, k; i < webStorage.length; i++) {
                    // #10: `k` may be an empty string
                    (k = webStorage.key(i)) && 'ngStorage-' === k.slice(0, 10) && ($storage[k.slice(10)] = angular.fromJson(webStorage.getItem(k)));
                }

                _last$storage = angular.copy($storage);

                $browser.addPollFn(function() {
                    if (!angular.equals($storage, _last$storage)) {
                        angular.forEach($storage, function(v, k) {
                            if (angular.isDefined(v) && '$' !== k[0]) {

                                // Remove $$hashKey and other things that cannot be stringified
                                $storage[k] = angular.fromJson(angular.toJson(v));

                                webStorage.setItem('ngStorage-' + k, angular.toJson(v));
                            }

                            delete _last$storage[k];
                        });

                        for (var k in _last$storage) {
                            webStorage.removeItem('ngStorage-' + k);
                        }

                        _last$storage = angular.copy($storage);

                        $rootScope.$apply();
                    }
                });

                // #6: Use `$window.addEventListener` instead of `angular.element` to avoid the jQuery-specific `event.originalEvent`
                'localStorage' === storageType && $window.addEventListener('storage', function(event) {
                    if ('ngStorage-' === event.key.slice(0, 10)) {
                        event.newValue ? $storage[event.key.slice(10)] = angular.fromJson(event.newValue) : delete $storage[event.key.slice(10)];

                        _last$storage = angular.copy($storage);

                        $rootScope.$apply();
                    }
                });

                return $storage;
            }
        ];
    }

})();
<|endoftext|>


<commit_before>(function(template, osmplayer) {

  /** The osmplayer namespace. */
  var osmplayer = osmplayer || {};

  // Define the busy object.
  osmplayer.playlist = osmplayer.playlist || {};

  // constructor.
  osmplayer.playlist[template] = function(context, options) {

    // Derive from playlist
    osmplayer.playlist.call(this, context, options);
  };

  // Define the prototype for all controllers.
  osmplayer.playlist[template].prototype = new osmplayer.playlist();
  osmplayer.playlist[template].prototype.constructor = osmplayer.playlist[template];

  /**
   * @see minplayer.plugin#construct
   */
  osmplayer.playlist[template].prototype.construct = function() {

    this.options = jQuery.extend({
      showPlaylist: true
    }, this.options);

    osmplayer.playlist.prototype.construct.call(this);

    // Show then hide the element.
    minplayer.showThenHide(this.elements.hideShow);

    // Make the main minplayer have the same width as the playlist.
    this.get('player', function(player) {

      // Set the size.
      var size = this.options.vertical ? 'width' : 'height';
      var position = this.options.vertical ? 'right' : 'bottom';
      var margin = this.options.vertical ? 'marginRight' : 'marginBottom';

      // Hide and show the playlist.
      this.hideShow = function(show, animate) {
        var playerPos = {}, displayPos = {};
        var displaySize = this.display[size]();
        var e = this.options.vertical ? 'e' : 's';
        var w = this.options.vertical ? 'w' : 'n';
        var from = show ? 'ui-icon-triangle-1-' + w : 'ui-icon-triangle-1-' + e;
        var to = show ? 'ui-icon-triangle-1-' + e : 'ui-icon-triangle-1-' + w;
        jQuery('span', this.elements.hideShow).removeClass(from).addClass(to);
        playerPos[position] = show ? displaySize : 0;
        if (animate) {
          player.elements.minplayer.animate(playerPos, 'fast');
        }
        else {
          player.elements.minplayer.css(playerPos);
        }
        displayPos[margin] = show ? 0 : -displaySize;
        if (animate) {
          this.display.animate(displayPos, 'fast', function() {
            player.resize();
          });
        }
        else {
          this.display.css(displayPos);
        }
      };

      // Bind when the playlist loads.
      this.bind('playlistLoad', (function(playlist) {
        return function(event, data) {
          if (data.nodes.length === 1) {
            playlist.hideShow(false, true);
          }
          else {
            playlist.hideShow(true, true);
          }
        };
      })(this));

      // Perform the show hide functionality of the playlist.
      if (this.elements.hideShow) {
        this.elements.hideShow.bind('click', (function(playlist) {
          return function(event) {
            event.preventDefault();
            var button = jQuery('span', playlist.elements.hideShow);
            var e = playlist.options.vertical ? 'e' : 's';
            var w = playlist.options.vertical ? 'w' : 'n';
            var show = button.hasClass('ui-icon-triangle-1-' + w);
            playlist.hideShow(show, true);
          };
        })(this));
      }

      // If they wish to show the playlist.
      if (player.elements.minplayer) {
        if (this.options.showPlaylist) {

          // Set the player to have the correct margin if the playlist is present.
          if (this.options.vertical) {
            player.elements.minplayer.css('right', this.display.width() + 'px');
          }
          else {
            player.elements.minplayer.css('bottom', this.display.height() + 'px');
          }
        }
        else {

          // Hide the playlist.
          this.hideShow(false);
        }
      }
    });
  };

  /**
   * Return the display for this plugin.
   */
  osmplayer.playlist[template].prototype.getDisplay = function() {
    if (this.options.build) {
      this.context.append('\

\

\
          \

\

\

\

\

\

\

\

');
    }
    return jQuery('.osmplayer-' + template + '-playlist', this.context);
  };

  // Return the elements
  osmplayer.playlist[template].prototype.getElements = function() {
    var elements = osmplayer.playlist.prototype.getElements.call(this);

    // Setup the dynamic settings.
    var cName = this.options.vertical ? 'playlist-vertical' : 'playlist-horizontal';
    cName += this.options.playlistOnly ? ' playlist-only' : '';
    var show = this.options.showPlaylist;
    var icon = this.options.vertical ? (show ? 'e' : 'w') : (show ? 's' : 'n');
    var corner = this.options.vertical ? 'ui-corner-left' : 'ui-corner-top';

    // Remove the playlist if we need to.
    if (this.options.disablePlaylist || !this.options.playlist) {
      this.display.remove();
    }

    this.display.addClass(cName);
    var hideShow = jQuery('.osmplayer-' + template + '-hide-show-playlist', this.display);
    hideShow.addClass(corner);
    if (this.options.playlistOnly) {
      hideShow.hide();
      hideShow = null;
    }
    jQuery('span', hideShow).addClass('ui-icon-triangle-1-' + icon);

    return jQuery.extend(elements, {
      playlist_busy:jQuery('.minplayer-' + template + '-loader-wrapper', this.display),
      list:jQuery('.osmplayer-' + template + '-playlist-list', this.display),
      scroll:jQuery('.osmplayer-' + template + '-playlist-scroll', this.display),
      hideShow: hideShow
    });
  };
})('default', osmplayer);

<commit_msg>Adding some protection around the minplayer in playlist code.<commit_after>(function(template, osmplayer) {

  /** The osmplayer namespace. */
  var osmplayer = osmplayer || {};

  // Define the busy object.
  osmplayer.playlist = osmplayer.playlist || {};

  // constructor.
  osmplayer.playlist[template] = function(context, options) {

    // Derive from playlist
    osmplayer.playlist.call(this, context, options);
  };

  // Define the prototype for all controllers.
  osmplayer.playlist[template].prototype = new osmplayer.playlist();
  osmplayer.playlist[template].prototype.constructor = osmplayer.playlist[template];

  /**
   * @see minplayer.plugin#construct
   */
  osmplayer.playlist[template].prototype.construct = function() {

    this.options = jQuery.extend({
      showPlaylist: true
    }, this.options);

    osmplayer.playlist.prototype.construct.call(this);

    // Show then hide the element.
    minplayer.showThenHide(this.elements.hideShow);

    // Make the main minplayer have the same width as the playlist.
    this.get('player', function(player) {

      // Set the size.
      var size = this.options.vertical ? 'width' : 'height';
      var position = this.options.vertical ? 'right' : 'bottom';
      var margin = this.options.vertical ? 'marginRight' : 'marginBottom';

      // Hide and show the playlist.
      this.hideShow = function(show, animate) {
        var playerPos = {}, displayPos = {};
        var displaySize = this.display[size]();
        var e = this.options.vertical ? 'e' : 's';
        var w = this.options.vertical ? 'w' : 'n';
        var from = show ? 'ui-icon-triangle-1-' + w : 'ui-icon-triangle-1-' + e;
        var to = show ? 'ui-icon-triangle-1-' + e : 'ui-icon-triangle-1-' + w;
        jQuery('span', this.elements.hideShow).removeClass(from).addClass(to);
        playerPos[position] = show ? displaySize : 0;
        if (player.elements.minplayer) {
          if (animate) {
            player.elements.minplayer.animate(playerPos, 'fast');
          }
          else {
            player.elements.minplayer.css(playerPos);
          }
        }
        displayPos[margin] = show ? 0 : -displaySize;
        if (animate) {
          this.display.animate(displayPos, 'fast', function() {
            player.resize();
          });
        }
        else {
          this.display.css(displayPos);
        }
      };

      // Bind when the playlist loads.
      this.bind('playlistLoad', (function(playlist) {
        return function(event, data) {
          if (data.nodes.length === 1) {
            playlist.hideShow(false, true);
          }
          else {
            playlist.hideShow(true, true);
          }
        };
      })(this));

      // Perform the show hide functionality of the playlist.
      if (this.elements.hideShow) {
        this.elements.hideShow.bind('click', (function(playlist) {
          return function(event) {
            event.preventDefault();
            var button = jQuery('span', playlist.elements.hideShow);
            var e = playlist.options.vertical ? 'e' : 's';
            var w = playlist.options.vertical ? 'w' : 'n';
            var show = button.hasClass('ui-icon-triangle-1-' + w);
            playlist.hideShow(show, true);
          };
        })(this));
      }

      // If they wish to show the playlist.
      if (player.elements.minplayer) {
        if (this.options.showPlaylist) {

          // Set the player to have the correct margin if the playlist is present.
          if (this.options.vertical) {
            player.elements.minplayer.css('right', this.display.width() + 'px');
          }
          else {
            player.elements.minplayer.css('bottom', this.display.height() + 'px');
          }
        }
        else {

          // Hide the playlist.
          this.hideShow(false);
        }
      }
    });
  };

  /**
   * Return the display for this plugin.
   */
  osmplayer.playlist[template].prototype.getDisplay = function() {
    if (this.options.build) {
      this.context.append('\

\

\
          \

\

\

\

\

\

\

\

');
    }
    return jQuery('.osmplayer-' + template + '-playlist', this.context);
  };

  // Return the elements
  osmplayer.playlist[template].prototype.getElements = function() {
    var elements = osmplayer.playlist.prototype.getElements.call(this);

    // Setup the dynamic settings.
    var cName = this.options.vertical ? 'playlist-vertical' : 'playlist-horizontal';
    cName += this.options.playlistOnly ? ' playlist-only' : '';
    var show = this.options.showPlaylist;
    var icon = this.options.vertical ? (show ? 'e' : 'w') : (show ? 's' : 'n');
    var corner = this.options.vertical ? 'ui-corner-left' : 'ui-corner-top';

    // Remove the playlist if we need to.
    if (this.options.disablePlaylist || !this.options.playlist) {
      this.display.remove();
    }

    this.display.addClass(cName);
    var hideShow = jQuery('.osmplayer-' + template + '-hide-show-playlist', this.display);
    hideShow.addClass(corner);
    if (this.options.playlistOnly) {
      hideShow.hide();
      hideShow = null;
    }
    jQuery('span', hideShow).addClass('ui-icon-triangle-1-' + icon);

    return jQuery.extend(elements, {
      playlist_busy:jQuery('.minplayer-' + template + '-loader-wrapper', this.display),
      list:jQuery('.osmplayer-' + template + '-playlist-list', this.display),
      scroll:jQuery('.osmplayer-' + template + '-playlist-scroll', this.display),
      hideShow: hideShow
    });
  };
})('default', osmplayer);

<|endoftext|>