This file is indexed.

/usr/share/javascript/yui3/app-base/app-base.js is in libjs-yui3-full 3.5.1-1ubuntu3.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

   1
   2
   3
   4
   5
   6
   7
   8
   9
  10
  11
  12
  13
  14
  15
  16
  17
  18
  19
  20
  21
  22
  23
  24
  25
  26
  27
  28
  29
  30
  31
  32
  33
  34
  35
  36
  37
  38
  39
  40
  41
  42
  43
  44
  45
  46
  47
  48
  49
  50
  51
  52
  53
  54
  55
  56
  57
  58
  59
  60
  61
  62
  63
  64
  65
  66
  67
  68
  69
  70
  71
  72
  73
  74
  75
  76
  77
  78
  79
  80
  81
  82
  83
  84
  85
  86
  87
  88
  89
  90
  91
  92
  93
  94
  95
  96
  97
  98
  99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
 197
 198
 199
 200
 201
 202
 203
 204
 205
 206
 207
 208
 209
 210
 211
 212
 213
 214
 215
 216
 217
 218
 219
 220
 221
 222
 223
 224
 225
 226
 227
 228
 229
 230
 231
 232
 233
 234
 235
 236
 237
 238
 239
 240
 241
 242
 243
 244
 245
 246
 247
 248
 249
 250
 251
 252
 253
 254
 255
 256
 257
 258
 259
 260
 261
 262
 263
 264
 265
 266
 267
 268
 269
 270
 271
 272
 273
 274
 275
 276
 277
 278
 279
 280
 281
 282
 283
 284
 285
 286
 287
 288
 289
 290
 291
 292
 293
 294
 295
 296
 297
 298
 299
 300
 301
 302
 303
 304
 305
 306
 307
 308
 309
 310
 311
 312
 313
 314
 315
 316
 317
 318
 319
 320
 321
 322
 323
 324
 325
 326
 327
 328
 329
 330
 331
 332
 333
 334
 335
 336
 337
 338
 339
 340
 341
 342
 343
 344
 345
 346
 347
 348
 349
 350
 351
 352
 353
 354
 355
 356
 357
 358
 359
 360
 361
 362
 363
 364
 365
 366
 367
 368
 369
 370
 371
 372
 373
 374
 375
 376
 377
 378
 379
 380
 381
 382
 383
 384
 385
 386
 387
 388
 389
 390
 391
 392
 393
 394
 395
 396
 397
 398
 399
 400
 401
 402
 403
 404
 405
 406
 407
 408
 409
 410
 411
 412
 413
 414
 415
 416
 417
 418
 419
 420
 421
 422
 423
 424
 425
 426
 427
 428
 429
 430
 431
 432
 433
 434
 435
 436
 437
 438
 439
 440
 441
 442
 443
 444
 445
 446
 447
 448
 449
 450
 451
 452
 453
 454
 455
 456
 457
 458
 459
 460
 461
 462
 463
 464
 465
 466
 467
 468
 469
 470
 471
 472
 473
 474
 475
 476
 477
 478
 479
 480
 481
 482
 483
 484
 485
 486
 487
 488
 489
 490
 491
 492
 493
 494
 495
 496
 497
 498
 499
 500
 501
 502
 503
 504
 505
 506
 507
 508
 509
 510
 511
 512
 513
 514
 515
 516
 517
 518
 519
 520
 521
 522
 523
 524
 525
 526
 527
 528
 529
 530
 531
 532
 533
 534
 535
 536
 537
 538
 539
 540
 541
 542
 543
 544
 545
 546
 547
 548
 549
 550
 551
 552
 553
 554
 555
 556
 557
 558
 559
 560
 561
 562
 563
 564
 565
 566
 567
 568
 569
 570
 571
 572
 573
 574
 575
 576
 577
 578
 579
 580
 581
 582
 583
 584
 585
 586
 587
 588
 589
 590
 591
 592
 593
 594
 595
 596
 597
 598
 599
 600
 601
 602
 603
 604
 605
 606
 607
 608
 609
 610
 611
 612
 613
 614
 615
 616
 617
 618
 619
 620
 621
 622
 623
 624
 625
 626
 627
 628
 629
 630
 631
 632
 633
 634
 635
 636
 637
 638
 639
 640
 641
 642
 643
 644
 645
 646
 647
 648
 649
 650
 651
 652
 653
 654
 655
 656
 657
 658
 659
 660
 661
 662
 663
 664
 665
 666
 667
 668
 669
 670
 671
 672
 673
 674
 675
 676
 677
 678
 679
 680
 681
 682
 683
 684
 685
 686
 687
 688
 689
 690
 691
 692
 693
 694
 695
 696
 697
 698
 699
 700
 701
 702
 703
 704
 705
 706
 707
 708
 709
 710
 711
 712
 713
 714
 715
 716
 717
 718
 719
 720
 721
 722
 723
 724
 725
 726
 727
 728
 729
 730
 731
 732
 733
 734
 735
 736
 737
 738
 739
 740
 741
 742
 743
 744
 745
 746
 747
 748
 749
 750
 751
 752
 753
 754
 755
 756
 757
 758
 759
 760
 761
 762
 763
 764
 765
 766
 767
 768
 769
 770
 771
 772
 773
 774
 775
 776
 777
 778
 779
 780
 781
 782
 783
 784
 785
 786
 787
 788
 789
 790
 791
 792
 793
 794
 795
 796
 797
 798
 799
 800
 801
 802
 803
 804
 805
 806
 807
 808
 809
 810
 811
 812
 813
 814
 815
 816
 817
 818
 819
 820
 821
 822
 823
 824
 825
 826
 827
 828
 829
 830
 831
 832
 833
 834
 835
 836
 837
 838
 839
 840
 841
 842
 843
 844
 845
 846
 847
 848
 849
 850
 851
 852
 853
 854
 855
 856
 857
 858
 859
 860
 861
 862
 863
 864
 865
 866
 867
 868
 869
 870
 871
 872
 873
 874
 875
 876
 877
 878
 879
 880
 881
 882
 883
 884
 885
 886
 887
 888
 889
 890
 891
 892
 893
 894
 895
 896
 897
 898
 899
 900
 901
 902
 903
 904
 905
 906
 907
 908
 909
 910
 911
 912
 913
 914
 915
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
/*
YUI 3.5.1 (build 22)
Copyright 2012 Yahoo! Inc. All rights reserved.
Licensed under the BSD License.
http://yuilibrary.com/license/
*/
YUI.add('app-base', function(Y) {

/**
The App Framework provides simple MVC-like building blocks (models, model lists,
views, and URL-based routing) for writing single-page JavaScript applications.

@main app
@module app
@since 3.4.0
**/

/**
Provides a top-level application component which manages navigation and views.

@module app
@submodule app-base
@since 3.5.0
**/

// TODO: Better handling of lifecycle for registered views:
//
//   * [!] Just redo basically everything with view management so there are no
//     pre-`activeViewChange` side effects and handle the rest of these things:
//
//   * Seems like any view created via `createView` should listen for the view's
//     `destroy` event and use that to remove it from the `_viewsInfoMap`. I
//     should look at what ModelList does for Models as a reference.
//
//   * Should we have a companion `destroyView()` method? Maybe this wouldn't be
//     needed if we have a `getView(name, create)` method, and already doing the
//     above? We could do `app.getView('foo').destroy()` and it would be removed
//     from the `_viewsInfoMap` as well.
//
//   * Should we wait to call a view's `render()` method inside of the
//     `_attachView()` method?
//
//   * Should named views support a collection of instances instead of just one?
//

var Lang    = Y.Lang,
    YObject = Y.Object,

    PjaxBase = Y.PjaxBase,
    Router   = Y.Router,
    View     = Y.View,

    getClassName = Y.ClassNameManager.getClassName,

    win = Y.config.win,

    App;

/**
Provides a top-level application component which manages navigation and views.

This gives you a foundation and structure on which to build your application; it
combines robust URL navigation with powerful routing and flexible view
management.

@class App.Base
@param {Object} [config] The following are configuration properties that can be
    specified _in addition_ to default attribute values and the non-attribute
    properties provided by `Y.Base`:
  @param {Object} [config.views] Hash of view-name to metadata used to
    declaratively describe an application's views and their relationship with
    the app and other views. The views specified here will override any defaults
    provided by the `views` object on the `prototype`.
@constructor
@extends Base
@uses View
@uses Router
@uses PjaxBase
@since 3.5.0
**/
App = Y.Base.create('app', Y.Base, [View, Router, PjaxBase], {
    // -- Public Properties ----------------------------------------------------

    /**
    Hash of view-name to metadata used to declaratively describe an
    application's views and their relationship with the app and its other views.

    The view metadata is composed of Objects keyed to a view-name that can have
    any or all of the following properties:

      * `type`: Function or a string representing the view constructor to use to
        create view instances. If a string is used, the constructor function is
        assumed to be on the `Y` object; e.g. `"SomeView"` -> `Y.SomeView`.

      * `preserve`: Boolean for whether the view instance should be retained. By
        default, the view instance will be destroyed when it is no longer the
        `activeView`. If `true` the view instance will simply be `removed()`
        from the DOM when it is no longer active. This is useful when the view
        is frequently used and may be expensive to re-create.

      * `parent`: String to another named view in this hash that represents the
        parent view within the application's view hierarchy; e.g. a `"photo"`
        view could have `"album"` has its `parent` view. This parent/child
        relationship is a useful cue for things like transitions.

      * `instance`: Used internally to manage the current instance of this named
        view. This can be used if your view instance is created up-front, or if
        you would rather manage the View lifecycle, but you probably should just
        let this be handled for you.

    If `views` are specified at instantiation time, the metadata in the `views`
    Object here will be used as defaults when creating the instance's `views`.

    Every `Y.App` instance gets its own copy of a `views` object so this Object
    on the prototype will not be polluted.

    @example
        // Imagine that `Y.UsersView` and `Y.UserView` have been defined.
        var app = new Y.App({
            views: {
                users: {
                    type    : Y.UsersView,
                    preserve: true
                },

                user: {
                    type  : Y.UserView,
                    parent: 'users'
                }
            }
        });

    @property views
    @type Object
    @default {}
    @since 3.5.0
    **/
    views: {},

    // -- Protected Properties -------------------------------------------------

    /**
    Map of view instance id (via `Y.stamp()`) to view-info object in `views`.

    This mapping is used to tie a specific view instance back to its metadata by
    adding a reference to the the related view info on the `views` object.

    @property _viewInfoMap
    @type Object
    @default {}
    @protected
    @since 3.5.0
    **/

    // -- Lifecycle Methods ----------------------------------------------------
    initializer: function (config) {
        config || (config = {});

        var views = {};

        // Merges-in specified view metadata into local `views` object.
        function mergeViewConfig(view, name) {
            views[name] = Y.merge(views[name], view);
        }

        // First, each view in the `views` prototype object gets its metadata
        // merged-in, providing the defaults.
        YObject.each(this.views, mergeViewConfig);

        // Then, each view in the specified `config.views` object gets its
        // metadata merged-in.
        YObject.each(config.views, mergeViewConfig);

        // The resulting hodgepodge of metadata is then stored as the instance's
        // `views` object, and no one's objects were harmed in the making.
        this.views        = views;
        this._viewInfoMap = {};

        // Using `bind()` to aid extensibility.
        this.after('activeViewChange', Y.bind('_afterActiveViewChange', this));

        // PjaxBase will bind click events when `html5` is `true`, so this just
        // forces the binding when `serverRouting` and `html5` are both falsy.
        if (!this.get('serverRouting')) {
            this._pjaxBindUI();
        }
    },

    // TODO: `destructor` to destroy the `activeView`?

    // -- Public Methods -------------------------------------------------------

    /**
    Creates and returns a new view instance using the provided `name` to look up
    the view info metadata defined in the `views` object. The passed-in `config`
    object is passed to the view constructor function.

    This function also maps a view instance back to its view info metadata.

    @method createView
    @param {String} name The name of a view defined on the `views` object.
    @param {Object} [config] The configuration object passed to the view
      constructor function when creating the new view instance.
    @return {View} The new view instance.
    @since 3.5.0
    **/
    createView: function (name, config) {
        var viewInfo = this.getViewInfo(name),
            type     = (viewInfo && viewInfo.type) || View,
            ViewConstructor, view;

        // Looks for a namespaced constructor function on `Y`.
        ViewConstructor = Lang.isString(type) ?
                YObject.getValue(Y, type.split('.')) : type;

        // Create the view instance and map it with its metadata.
        view = new ViewConstructor(config);
        this._viewInfoMap[Y.stamp(view, true)] = viewInfo;

        return view;
    },

    /**
    Returns the metadata associated with a view instance or view name defined on
    the `views` object.

    @method getViewInfo
    @param {View|String} view View instance, or name of a view defined on the
      `views` object.
    @return {Object} The metadata for the view, or `undefined` if the view is
      not registered.
    @since 3.5.0
    **/
    getViewInfo: function (view) {
        if (Lang.isString(view)) {
            return this.views[view];
        }

        return view && this._viewInfoMap[Y.stamp(view, true)];
    },

    /**
    Navigates to the specified URL if there is a route handler that matches. In
    browsers capable of using HTML5 history or when `serverRouting` is falsy,
    the navigation will be enhanced by firing the `navigate` event and having
    the app handle the "request". When `serverRouting` is `true`, non-HTML5
    browsers will navigate to the new URL via a full page reload.

    When there is a route handler for the specified URL and it is being
    navigated to, this method will return `true`, otherwise it will return
    `false`.

    **Note:** The specified URL _must_ be of the same origin as the current URL,
    otherwise an error will be logged and navigation will not occur. This is
    intended as both a security constraint and a purposely imposed limitation as
    it does not make sense to tell the app to navigate to a URL on a
    different scheme, host, or port.

    @method navigate
    @param {String} url The URL to navigate to. This must be of the same origin
      as the current URL.
    @param {Object} [options] Additional options to configure the navigation.
      These are mixed into the `navigate` event facade.
        @param {Boolean} [options.replace] Whether or not the current history
          entry will be replaced, or a new entry will be created. Will default
          to `true` if the specified `url` is the same as the current URL.
        @param {Boolean} [options.force] Whether the enhanced navigation
          should occur even in browsers without HTML5 history. Will default to
          `true` when `serverRouting` is falsy.
    @see PjaxBase.navigate()
    **/
    // Does not override `navigate()` but does use extra `options`.

    /**
    Renders this application by appending the `viewContainer` node to the
    `container` node if it isn't already a child of the container, and the
    `activeView` will be appended the view container, if it isn't already.

    You should call this method at least once, usually after the initialization
    of your app instance so the proper DOM structure is setup and optionally
    append the container to the DOM if it's not there already.

    You may override this method to customize the app's rendering, but you
    should expect that the `viewContainer`'s contents will be modified by the
    app for the purpose of rendering the `activeView` when it changes.

    @method render
    @chainable
    @see View.render()
    **/
    render: function () {
        var container           = this.get('container'),
            viewContainer       = this.get('viewContainer'),
            activeView          = this.get('activeView'),
            activeViewContainer = activeView && activeView.get('container'),
            areSame             = container.compareTo(viewContainer);

        container.addClass(App.CSS_CLASS);
        viewContainer.addClass(App.VIEWS_CSS_CLASS);

        // Prevents needless shuffling around of nodes and maintains DOM order.
        if (activeView && !viewContainer.contains(activeViewContainer)) {
            viewContainer.appendChild(activeViewContainer);
        }

        // Prevents needless shuffling around of nodes and maintains DOM order.
        if (!container.contains(viewContainer) && !areSame) {
            container.appendChild(viewContainer);
        }

        return this;
    },

    /**
    Sets which view is active/visible for the application. This will set the
    app's `activeView` attribute to the specified `view`.

    The `view` will be "attached" to this app, meaning it will be both rendered
    into this app's `viewContainer` node and all of its events will bubble to
    the app. The previous `activeView` will be "detached" from this app.

    When a string-name is provided for a view which has been registered on this
    app's `views` object, the referenced metadata will be used and the
    `activeView` will be set to either a preserved view instance, or a new
    instance of the registered view will be created using the specified `config`
    object passed-into this method.

    A callback function can be specified as either the third or fourth argument,
    and this function will be called after the new `view` becomes the
    `activeView`, is rendered to the `viewContainer`, and is ready to use.

    @example
        var app = new Y.App({
            views: {
                usersView: {
                    // Imagine that `Y.UsersView` has been defined.
                    type: Y.UsersView
                }
            },

            users: new Y.ModelList()
        });

        app.route('/users/', function () {
            this.showView('usersView', {users: this.get('users')});
        });

        app.render();
        app.navigate('/uses/'); // => Creates a new `Y.UsersView` and shows it.

    @method showView
    @param {String|View} view The name of a view defined in the `views` object,
        or a view instance which should become this app's `activeView`.
    @param {Object} [config] Optional configuration to use when creating a new
        view instance. This config object can also be used to update an existing
        or preserved view's attributes when `options.update` is `true`.
    @param {Object} [options] Optional object containing any of the following
        properties:
      @param {Function} [options.callback] Optional callback function to call
        after new `activeView` is ready to use, the function will be passed:
          @param {View} options.callback.view A reference to the new
            `activeView`.
      @param {Boolean} [options.prepend=false] Whether the `view` should be
        prepended instead of appended to the `viewContainer`.
      @param {Boolean} [options.render] Whether the `view` should be rendered.
        **Note:** If no value is specified, a view instance will only be
        rendered if it's newly created by this method.
      @param {Boolean} [options.update=false] Whether an existing view should
        have its attributes updated by passing the `config` object to its
        `setAttrs()` method. **Note:** This option does not have an effect if
        the `view` instance is created as a result of calling this method.
    @param {Function} [callback] Optional callback Function to call after the
        new `activeView` is ready to use. **Note:** this will override
        `options.callback` and it can be specified as either the third or fourth
        argument. The function will be passed the following:
      @param {View} callback.view A reference to the new `activeView`.
    @chainable
    @since 3.5.0
    **/
    showView: function (view, config, options, callback) {
        var viewInfo, created;

        options || (options = {});

        // Support the callback function being either the third or fourth arg.
        if (callback) {
            options.callback = callback;
        } else if (Lang.isFunction(options)) {
            options = {callback: options};
        }

        if (Lang.isString(view)) {
            viewInfo = this.getViewInfo(view);

            // Use the preserved view instance, or create a new view.
            // TODO: Maybe we can remove the strict check for `preserve` and
            // assume we'll use a View instance if it is there, and just check
            // `preserve` when detaching?
            if (viewInfo && viewInfo.preserve && viewInfo.instance) {
                view = viewInfo.instance;

                // Make sure there's a mapping back to the view metadata.
                this._viewInfoMap[Y.stamp(view, true)] = viewInfo;
            } else {
                // TODO: Add the app as a bubble target during construction, but
                // make sure to check that it isn't already in `bubbleTargets`!
                // This will allow the app to be notified for about _all_ of the
                // view's events. **Note:** This should _only_ happen if the
                // view is created _after_ `activeViewChange`.

                view    = this.createView(view, config);
                created = true;
            }
        }

        // Update the specified or preserved `view` when signaled to do so.
        // There's no need to updated a view if it was _just_ created.
        if (options.update && !created) {
            view.setAttrs(config);
        }

        // TODO: Hold off on rendering the view until after it has been
        // "attached", and move the call to render into `_attachView()`.

        // When a value is specified for `options.render`, prefer it because it
        // represents the developer's intent. When no value is specified, the
        // `view` will only be rendered if it was just created.
        if ('render' in options) {
            options.render && view.render();
        } else if (created) {
            view.render();
        }

        return this._set('activeView', view, {options: options});
    },

    // -- Protected Methods ----------------------------------------------------

    /**
    Helper method to attach the view instance to the application by making the
    app a bubble target of the view, append the view to the `viewContainer`, and
    assign it to the `instance` property of the associated view info metadata.

    @method _attachView
    @param {View} view View to attach.
    @param {Boolean} prepend=false Whether the view should be prepended instead
      of appended to the `viewContainer`.
    @protected
    @since 3.5.0
    **/
    _attachView: function (view, prepend) {
        if (!view) {
            return;
        }

        var viewInfo      = this.getViewInfo(view),
            viewContainer = this.get('viewContainer');

        view.addTarget(this);
        viewInfo && (viewInfo.instance = view);

        // TODO: Attach events here for persevered Views?
        // See related TODO in `_detachView`.

        // TODO: Actually render the view here so that it gets "attached" before
        // it gets rendered?

        // Insert view into the DOM.
        viewContainer[prepend ? 'prepend' : 'append'](view.get('container'));
    },

    /**
    Overrides View's container destruction to deal with the `viewContainer` and
    checks to make sure not to remove and purge the `<body>`.

    @method _destroyContainer
    @protected
    @see View._destroyContainer()
    **/
    _destroyContainer: function () {
        var container     = this.get('container'),
            viewContainer = this.get('viewContainer'),
            areSame       = container.compareTo(viewContainer);

        // We do not want to remove or destroy the `<body>`.
        if (Y.one('body').compareTo(container)) {
            // Just clean-up our events listeners.
            this.detachEvents();

            // Clean-up `yui3-app` CSS class on the `container`.
            container && container.removeClass(App.CSS_CLASS);

            if (areSame) {
                // Clean-up `yui3-app-views` CSS class on the `container`.
                container && container.removeClass(App.VIEWS_CSS_CLASS);
            } else {
                // Destroy and purge the `viewContainer`.
                viewContainer && viewContainer.remove(true);
            }

            return;
        }

        // Remove and purge events from both containers.
        viewContainer && viewContainer.remove(true);
        !areSame && container && container.remove(true);
    },

    /**
    Helper method to detach the view instance from the application by removing
    the application as a bubble target of the view, and either just removing the
    view if it is intended to be preserved, or destroying the instance
    completely.

    @method _detachView
    @param {View} view View to detach.
    @protected
    @since 3.5.0
    **/
    _detachView: function (view) {
        if (!view) {
            return;
        }

        var viewInfo = this.getViewInfo(view) || {};

        if (viewInfo.preserve) {
            view.remove();
            // TODO: Detach events here for preserved Views? It is possible that
            // some event subscriptions are made on elements other than the
            // View's `container`.
        } else {
            view.destroy({remove: true});

            // TODO: The following should probably happen automagically from
            // `destroy()` being called! Possibly `removeTarget()` as well.

            // Remove from view to view-info map.
            delete this._viewInfoMap[Y.stamp(view, true)];

            // Remove from view-info instance property.
            if (view === viewInfo.instance) {
                delete viewInfo.instance;
            }
        }

        view.removeTarget(this);
    },

    /**
    Getter for the `viewContainer` attribute.

    @method _getViewContainer
    @param {Node|null} value Current attribute value.
    @return {Node} View container node.
    @protected
    @since 3.5.0
    **/
    _getViewContainer: function (value) {
        // This wackiness is necessary to enable fully lazy creation of the
        // container node both when no container is specified and when one is
        // specified via a valueFn.

        if (!value && !this._viewContainer) {
            // Create a default container and set that as the new attribute
            // value. The `this._viewContainer` property prevents infinite
            // recursion.
            value = this._viewContainer = this.create();
            this._set('viewContainer', value);
        }

        return value;
    },

    /**
    Gets the current full URL. When `html5` is false, the URL will first be
    upgraded before it's returned.

    @method _getURL
    @return {String} URL.
    @protected
    @see Router._getURL()
    **/
    _getURL: function () {
        var url = Y.getLocation().toString();
        return this._html5 ? url : this._upgradeURL(url);
    },

    /**
    Provides the default value for the `html5` attribute.

    The value returned is dependent on the value of the `serverRouting`
    attribute. When `serverRouting` is explicit set to `false` (not just falsy),
    the default value for `html5` will be set to `false` for *all* browsers.

    When `serverRouting` is `true` or `undefined` the returned value will be
    dependent on the browser's capability of using HTML5 history.

    @method _initHtml5
    @return {Boolean} Whether or not HTML5 history should be used.
    @protected
    @since 3.5.0
    **/
    _initHtml5: function () {
        // When `serverRouting` is explicitly set to `false` (not just falsy),
        // forcing hash-based URLs in all browsers.
        if (this.get('serverRouting') === false) {
            return false;
        } else {
            return Router.html5;
        }
    },

    /**
    Determines if the specified `view` is configured as a child of the specified
    `parent` view. This requires both views to be either named-views, or view
    instances created using configuration data that exists in the `views`
    object, e.g. created by the `createView()` or `showView()` method.

    @method _isChildView
    @param {View|String} view The name of a view defined in the `views` object,
      or a view instance.
    @param {View|String} parent The name of a view defined in the `views`
      object, or a view instance.
    @return {Boolean} Whether the view is configured as a child of the parent.
    @protected
    @since 3.5.0
    **/
    _isChildView: function (view, parent) {
        var viewInfo   = this.getViewInfo(view),
            parentInfo = this.getViewInfo(parent);

        if (viewInfo && parentInfo) {
            return this.getViewInfo(viewInfo.parent) === parentInfo;
        }

        return false;
    },

    /**
    Determines if the specified `view` is configured as the parent of the
    specified `child` view. This requires both views to be either named-views,
    or view instances created using configuration data that exists in the
    `views` object, e.g. created by the `createView()` or `showView()` method.

    @method _isParentView
    @param {View|String} view The name of a view defined in the `views` object,
      or a view instance.
    @param {View|String} parent The name of a view defined in the `views`
      object, or a view instance.
    @return {Boolean} Whether the view is configured as the parent of the child.
    @protected
    @since 3.5.0
    **/
    _isParentView: function (view, child) {
        var viewInfo  = this.getViewInfo(view),
            childInfo = this.getViewInfo(child);

        if (viewInfo && childInfo) {
            return this.getViewInfo(childInfo.parent) === viewInfo;
        }

        return false;
    },

    /**
    Underlying implementation for `navigate()`.

    @method _navigate
    @param {String} url The fully-resolved URL that the app should dispatch to
      its route handlers to fulfill the enhanced navigation "request", or use to
      update `window.location` in non-HTML5 history capable browsers when
      `serverRouting` is `true`.
    @param {Object} [options] Additional options to configure the navigation.
      These are mixed into the `navigate` event facade.
        @param {Boolean} [options.replace] Whether or not the current history
          entry will be replaced, or a new entry will be created. Will default
          to `true` if the specified `url` is the same as the current URL.
        @param {Boolean} [options.force] Whether the enhanced navigation
          should occur even in browsers without HTML5 history. Will default to
          `true` when `serverRouting` is falsy.
    @protected
    @see PjaxBase._navigate()
    **/
    _navigate: function (url, options) {
        url = this._upgradeURL(url);

        options || (options = {});

        if (!this.get('serverRouting')) {
            // Force navigation to be enhanced and handled by the app when
            // `serverRouting` is falsy because the server might not be able to
            // properly handle the request.
            'force' in options || (options.force = true);
        }

        return PjaxBase.prototype._navigate.call(this, url, options);
    },

    /**
    Will either save a history entry using `pushState()` or the location hash,
    or gracefully-degrade to sending a request to the server causing a full-page
    reload.

    Overrides Router's `_save()` method to preform graceful-degradation when the
    app's `serverRouting` is `true` and `html5` is `false` by updating the full
    URL via standard assignment to `window.location` or by calling
    `window.location.replace()`; both of which will cause a request to the
    server resulting in a full-page reload.

    Otherwise this will just delegate off to Router's `_save()` method allowing
    the client-side enhanced routing to occur.

    @method _save
    @param {String} [url] URL for the history entry.
    @param {Boolean} [replace=false] If `true`, the current history entry will
      be replaced instead of a new one being added.
    @chainable
    @protected
    @see Router._save()
    **/
    _save: function (url, replace) {
        // Forces full-path URLs to always be used by modifying
        // `window.location` in non-HTML5 history capable browsers.
        if (this.get('serverRouting') && !this.get('html5')) {
            // Perform same-origin check on the specified URL.
            if (!this._hasSameOrigin(url)) {
                Y.error('Security error: The new URL must be of the same origin as the current URL.');
                return this;
            }

            // Results in the URL's full path starting with '/'.
            url = this._joinURL(url || '');

            // Either replace the current history entry or create a new one
            // while navigating to the `url`.
            if (replace) {
                win && win.location.replace(url);
            } else {
                win && (win.location = url);
            }

            return this;
        }

        return Router.prototype._save.apply(this, arguments);
    },

    /**
    Performs the actual change of this app's `activeView` by attaching the
    `newView` to this app, and detaching the `oldView` from this app using any
    specified `options`.

    The `newView` is attached to the app by rendering it to the `viewContainer`,
    and making this app a bubble target of its events.

    The `oldView` is detached from the app by removing it from the
    `viewContainer`, and removing this app as a bubble target for its events.
    The `oldView` will either be preserved or properly destroyed.

    **Note:** The `activeView` attribute is read-only and can be changed by
    calling the `showView()` method.

    @method _uiSetActiveView
    @param {View} newView The View which is now this app's `activeView`.
    @param {View} [oldView] The View which was this app's `activeView`.
    @param {Object} [options] Optional object containing any of the following
        properties:
      @param {Function} [options.callback] Optional callback function to call
        after new `activeView` is ready to use, the function will be passed:
          @param {View} options.callback.view A reference to the new
            `activeView`.
      @param {Boolean} [options.prepend=false] Whether the `view` should be
        prepended instead of appended to the `viewContainer`.
      @param {Boolean} [options.render] Whether the `view` should be rendered.
        **Note:** If no value is specified, a view instance will only be
        rendered if it's newly created by this method.
      @param {Boolean} [options.update=false] Whether an existing view should
        have its attributes updated by passing the `config` object to its
        `setAttrs()` method. **Note:** This option does not have an effect if
        the `view` instance is created as a result of calling this method.
    @protected
    @since 3.5.0
    **/
    _uiSetActiveView: function (newView, oldView, options) {
        options || (options = {});

        var callback = options.callback,
            isChild  = this._isChildView(newView, oldView),
            isParent = !isChild && this._isParentView(newView, oldView),
            prepend  = !!options.prepend || isParent;

        // Prevent detaching (thus removing) the view we want to show. Also hard
        // to animate out and in, the same view.
        if (newView === oldView) {
            return callback && callback.call(this, newView);
        }

        this._attachView(newView, prepend);
        this._detachView(oldView);

        callback && callback.call(this, newView);
    },

    /**
    Upgrades a hash-based URL to a full-path URL, if necessary.

    The specified `url` will be upgraded if its of the same origin as the
    current URL and has a path-like hash. URLs that don't need upgrading will be
    returned as-is.

    @example
        app._upgradeURL('http://example.com/#/foo/'); // => 'http://example.com/foo/';

    @method _upgradeURL
    @param {String} url The URL to upgrade from hash-based to full-path.
    @return {String} The upgraded URL, or the specified URL untouched.
    @protected
    @since 3.5.0
    **/
    _upgradeURL: function (url) {
        // We should not try to upgrade paths for external URLs.
        if (!this._hasSameOrigin(url)) {
            return url;
        }

        // TODO: Should the `root` be removed first, and the hash only
        // considered if in the form of '/#/'?
        var hash       = (url.match(/#(.*)$/) || [])[1] || '',
            hashPrefix = Y.HistoryHash.hashPrefix;

        // Strip any hash prefix, like hash-bangs.
        if (hashPrefix && hash.indexOf(hashPrefix) === 0) {
            hash = hash.replace(hashPrefix, '');
        }

        // If the hash looks like a URL path, assume it is, and upgrade it!
        if (hash && hash.charAt(0) === '/') {
            // Re-join with configured `root` before resolving.
            url = this._resolveURL(this._joinURL(hash));
        }

        return url;
    },

    // -- Protected Event Handlers ---------------------------------------------

    /**
    Handles the application's `activeViewChange` event (which is fired when the
    `activeView` attribute changes) by detaching the old view, attaching the new
    view.

    The `activeView` attribute is read-only, so the public API to change its
    value is through the `showView()` method.

    @method _afterActiveViewChange
    @param {EventFacade} e
    @protected
    @since 3.5.0
    **/
    _afterActiveViewChange: function (e) {
        this._uiSetActiveView(e.newVal, e.prevVal, e.options);
    }
}, {
    ATTRS: {
        /**
        The application's active/visible view.

        This attribute is read-only, to set the `activeView` use the
        `showView()` method.

        @attribute activeView
        @type View
        @default null
        @readOnly
        @see App.Base.showView()
        @since 3.5.0
        **/
        activeView: {
            value   : null,
            readOnly: true
        },

        /**
        Container node which represents the application's bounding-box, into
        which this app's content will be rendered.

        The container node serves as the host for all DOM events attached by the
        app. Delegation is used to handle events on children of the container,
        allowing the container's contents to be re-rendered at any time without
        losing event subscriptions.

        The default container is the `<body>` Node, but you can override this in
        a subclass, or by passing in a custom `container` config value at
        instantiation time.

        When `container` is overridden by a subclass or passed as a config
        option at instantiation time, it may be provided as a selector string, a
        DOM element, or a `Y.Node` instance. During initialization, this app's
        `create()` method will be called to convert the container into a
        `Y.Node` instance if it isn't one already and stamp it with the CSS
        class: `"yui3-app"`.

        The container is not added to the page automatically. This allows you to
        have full control over how and when your app is actually rendered to
        the page.

        @attribute container
        @type HTMLElement|Node|String
        @default Y.one('body')
        @initOnly
        **/
        container: {
            valueFn: function () {
                return Y.one('body');
            }
        },

        /**
        Whether or not this browser is capable of using HTML5 history.

        This value is dependent on the value of `serverRouting` and will default
        accordingly.

        Setting this to `false` will force the use of hash-based history even on
        HTML5 browsers, but please don't do this unless you understand the
        consequences.

        @attribute html5
        @type Boolean
        @initOnly
        @see serverRouting
        **/
        html5: {
            valueFn: '_initHtml5'
        },

        /**
        CSS selector string used to filter link click events so that only the
        links which match it will have the enhanced-navigation behavior of pjax
        applied.

        When a link is clicked and that link matches this selector, navigating
        to the link's `href` URL using the enhanced, pjax, behavior will be
        attempted; and the browser's default way to navigate to new pages will
        be the fallback.

        By default this selector will match _all_ links on the page.

        @attribute linkSelector
        @type String|Function
        @default "a"
        **/
        linkSelector: {
            value: 'a'
        },

        /**
        Whether or not this application's server is capable of properly routing
        all requests and rendering the initial state in the HTML responses.

        This can have three different values, each having particular
        implications on how the app will handle routing and navigation:

          * `undefined`: The best form of URLs will be chosen based on the
            capabilities of the browser. Given no information about the server
            environmentm a balanced approach to routing and navigation is
            chosen.

            The server should be capable of handling full-path requests, since
            full-URLs will be generated by browsers using HTML5 history. If this
            is a client-side-only app the server could handle full-URL requests
            by sending a redirect back to the root with a hash-based URL, e.g:

                Request:     http://example.com/users/1
                Redirect to: http://example.com/#/users/1

          * `true`: The server is *fully* capable of properly handling requests
            to all full-path URLs the app can produce.

            This is the best option for progressive-enhancement because it will
            cause **all URLs to always have full-paths**, which means the server
            will be able to accurately handle all URLs this app produces. e.g.

                http://example.com/users/1

            To meet this strict full-URL requirement, browsers which are not
            capable of using HTML5 history will make requests to the server
            resulting in full-page reloads.

          * `false`: The server is *not* capable of properly handling requests
            to all full-path URLs the app can produce, therefore all routing
            will be handled by this App instance.

            Be aware that this will cause **all URLs to always be hash-based**,
            even in browsers that are capable of using HTML5 history. e.g.

                http://example.com/#/users/1

            A single-page or client-side-only app where the server sends a
            "shell" page with JavaScript to the client might have this
            restriction. If you're setting this to `false`, read the following:

        **Note:** When this is set to `false`, the server will *never* receive
        the full URL because browsers do not send the fragment-part to the
        server, that is everything after and including the "#".

        Consider the following example:

            URL shown in browser: http://example.com/#/users/1
            URL sent to server:   http://example.com/

        You should feel bad about hurting our precious web if you forcefully set
        either `serverRouting` or `html5` to `false`, because you're basically
        punching the web in the face here with your lossy URLs! Please make sure
        you know what you're doing and that you understand the implications.

        Ideally you should always prefer full-path URLs (not /#/foo/), and want
        full-page reloads when the client's browser is not capable of enhancing
        the experience using the HTML5 history APIs. Setting this to `true` is
        the best option for progressive-enhancement (and graceful-degradation).

        @attribute serverRouting
        @type Boolean
        @default undefined
        @initOnly
        @since 3.5.0
        **/
        serverRouting: {
            value    : undefined,
            writeOnce: 'initOnly'
        },

        /**
        The node into which this app's `views` will be rendered when they become
        the `activeView`.

        The view container node serves as the container to hold the app's
        `activeView`. Each time the `activeView` is set via `showView()`, the
        previous view will be removed from this node, and the new active view's
        `container` node will be appended.

        The default view container is a `<div>` Node, but you can override this
        in a subclass, or by passing in a custom `viewContainer` config value at
        instantiation time. The `viewContainer` may be provided as a selector
        string, DOM element, or a `Y.Node` instance (having the `viewContainer`
        and the `container` be the same node is also supported).

        The app's `render()` method will stamp the view container with the CSS
        class `"yui3-app-views"` and append it to the app's `container` node if
        it isn't already, and any `activeView` will be appended to this node if
        it isn't already.

        @attribute viewContainer
        @type HTMLElement|Node|String
        @default Y.Node.create(this.containerTemplate)
        @initOnly
        @since 3.5.0
        **/
        viewContainer: {
            getter   : '_getViewContainer',
            setter   : Y.one,
            writeOnce: true
        }
    },

    // TODO: Should these go on the `prototype`?
    // TODO: These should also just go in a `CLASS_NAMES` object.

    /**
    CSS class added to an app's `container` node.

    @property CSS_CLASS
    @type String
    @default "yui3-app"
    @static
    @since 3.5.0
    **/
    CSS_CLASS: getClassName('app'),

    /**
    CSS class added to an app's `viewContainer` node.

    @property VIEWS_CSS_CLASS
    @type String
    @default "yui3-app-views"
    @static
    @since 3.5.0
    **/
    VIEWS_CSS_CLASS: getClassName('app', 'views'),

    /**
    Properties that shouldn't be turned into ad-hoc attributes when passed to
    App's constructor.

    @property _NON_ATTRS_CFG
    @type Array
    @static
    @protected
    @since 3.5.0
    **/
    _NON_ATTRS_CFG: ['views']
});

// -- Namespace ----------------------------------------------------------------
Y.namespace('App').Base = App;

/**
Provides a top-level application component which manages navigation and views.

This gives you a foundation and structure on which to build your application; it
combines robust URL navigation with powerful routing and flexible view
management.

`Y.App` is both a namespace and constructor function. The `Y.App` class is
special in that any `Y.App` class extensions that are included in the YUI
instance will be **auto-mixed** on to the `Y.App` class. Consider this example:

    YUI().use('app-base', 'app-transitions', function (Y) {
        // This will create two YUI Apps, `basicApp` will not have transitions,
        // but `fancyApp` will have transitions support included and turn it on.
        var basicApp = new Y.App.Base(),
            fancyApp = new Y.App({transitions: true});
    });

@class App
@param {Object} [config] The following are configuration properties that can be
    specified _in addition_ to default attribute values and the non-attribute
    properties provided by `Y.Base`:
  @param {Object} [config.views] Hash of view-name to metadata used to
    declaratively describe an application's views and their relationship with
    the app and other views. The views specified here will override any defaults
    provided by the `views` object on the `prototype`.
@constructor
@extends App.Base
@uses App.Transitions
@since 3.5.0
**/
Y.App = Y.mix(Y.Base.create('app', Y.App.Base, []), Y.App, true);


}, '3.5.1' ,{requires:['classnamemanager', 'pjax-base', 'router', 'view']});