@breakside/jskit 2022.24.0 → 2022.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/Frameworks/DOM.jsframework/Info.json +2 -2
  2. package/Frameworks/DOM.jsframework/io.breakside.JSKit.DOM-bundle.js +2 -2
  3. package/Frameworks/FontKit.jsframework/Info.json +2 -2
  4. package/Frameworks/FontKit.jsframework/io.breakside.JSKit.FontKit-bundle.js +2 -2
  5. package/Frameworks/Foundation.jsframework/Info.json +2 -2
  6. package/Frameworks/Foundation.jsframework/JS/JSFileManager+Node.js +2 -2
  7. package/Frameworks/Foundation.jsframework/JS/JSURL.js +3 -0
  8. package/Frameworks/Foundation.jsframework/JS/JSZip.js +97 -76
  9. package/Frameworks/Foundation.jsframework/io.breakside.JSKit.Foundation-bundle.js +2 -2
  10. package/Frameworks/SecurityKit.jsframework/Info.json +2 -2
  11. package/Frameworks/SecurityKit.jsframework/io.breakside.JSKit.SecurityKit-bundle.js +2 -2
  12. package/Info.json +2 -2
  13. package/Node/io.breakside.jskit-bundle.js +2 -2
  14. package/Root/Frameworks/APIKit/Info.yaml +1 -1
  15. package/Root/Frameworks/APIKitTesting/Info.yaml +1 -1
  16. package/Root/Frameworks/AuthKit/Info.yaml +1 -1
  17. package/Root/Frameworks/CSSOM/Info.yaml +1 -1
  18. package/Root/Frameworks/ChartKit/Info.yaml +1 -1
  19. package/Root/Frameworks/ConferenceKit/Info.yaml +1 -1
  20. package/Root/Frameworks/DBKit/Info.yaml +1 -1
  21. package/Root/Frameworks/DOM/Info.yaml +1 -1
  22. package/Root/Frameworks/Dispatch/Info.yaml +1 -1
  23. package/Root/Frameworks/FontKit/Info.yaml +1 -1
  24. package/Root/Frameworks/Foundation/Info.yaml +1 -1
  25. package/Root/Frameworks/Foundation/JSFileManager+Node.js +2 -2
  26. package/Root/Frameworks/Foundation/JSURL.js +3 -0
  27. package/Root/Frameworks/Foundation/JSZip.js +97 -76
  28. package/Root/Frameworks/ImageKit/Info.yaml +1 -1
  29. package/Root/Frameworks/MediaKit/Info.yaml +1 -1
  30. package/Root/Frameworks/MediaKitUI/Info.yaml +1 -1
  31. package/Root/Frameworks/NotificationKit/Info.yaml +1 -1
  32. package/Root/Frameworks/PDFKit/Info.yaml +1 -1
  33. package/Root/Frameworks/QRKit/Info.yaml +1 -1
  34. package/Root/Frameworks/SearchKit/Info.yaml +1 -1
  35. package/Root/Frameworks/SecurityKit/Info.yaml +1 -1
  36. package/Root/Frameworks/ServerKit/Info.yaml +1 -1
  37. package/Root/Frameworks/ServerKitTesting/Info.yaml +1 -1
  38. package/Root/Frameworks/TestKit/Info.yaml +1 -1
  39. package/Root/Frameworks/UIKit/Info.yaml +1 -1
  40. package/Root/Frameworks/UIKit/UIBasicAnimation.js +1 -1
  41. package/Root/Frameworks/UIKit/UICursor.js +19 -4
  42. package/Root/Frameworks/UIKit/UIHTMLApplication.js +53 -15
  43. package/Root/Frameworks/UIKit/UIHTMLDisplayServerCanvasContext.js +2 -48
  44. package/Root/Frameworks/UIKit/UIHTMLDisplayServerContext.js +0 -6
  45. package/Root/Frameworks/UIKit/UIHTMLDisplayServerSVGContext.js +6 -77
  46. package/Root/Frameworks/UIKit/UIHTMLTextField.js +9 -3
  47. package/Root/Frameworks/UIKit/UIHTMLTextTypesetter.js +2 -29
  48. package/Root/Frameworks/UIKit/UIHTMLWindowServer.js +6 -79
  49. package/Root/Frameworks/UIKit/UIImageLayer.js +14 -7
  50. package/Root/Frameworks/UIKit/UILabel.js +74 -1
  51. package/Root/Frameworks/UIKit/UIMouseTrackingArea.js +55 -0
  52. package/Root/Frameworks/UIKit/UIState.js +23 -0
  53. package/Root/Frameworks/UIKit/UITextEditor.js +277 -76
  54. package/Root/Frameworks/UIKit/UITextEditorDelegate.js +3 -1
  55. package/Root/Frameworks/UIKit/UITextLayer.js +1 -1
  56. package/Root/Frameworks/UIKit/UIView.js +118 -46
  57. package/Root/Frameworks/UIKit/UIViewController.js +6 -0
  58. package/Root/Frameworks/UIKit/UIWindow.js +89 -0
  59. package/Root/Frameworks/UIKit/UIWindowServer.js +20 -79
  60. package/Root/Frameworks/UIKitTesting/Info.yaml +1 -1
  61. package/package.json +1 -1
@@ -1,6 +1,9 @@
1
1
  // #import "JSObject.js"
2
2
  // #import "Promise+JS.js"
3
3
  // #import "JSCRC32.js"
4
+ // #import "JSDate.js"
5
+ // #import "JSCalendar.js"
6
+ // #import "Deflate.js"
4
7
  "use strict";
5
8
 
6
9
  JSClass("JSZip", JSObject, {
@@ -14,61 +17,89 @@ JSClass("JSZip", JSObject, {
14
17
  offset: 0,
15
18
 
16
19
  addFile: function(file, completion, target){
20
+ return this.addFileInDirectory(file, "", {}, completion, target);
21
+ },
22
+
23
+ addFileInDirectory: function(file, directory, attributes, completion, target){
17
24
  if (!completion){
18
25
  completion = Promise.completion(Promise.resolveNull);
19
26
  }
27
+ if (directory.startsWith("/")){
28
+ directory = directory.substr(1);
29
+ }
30
+ if (directory.length > 1 && !directory.endsWith("/")){
31
+ directory = directory + "/";
32
+ }
20
33
  file.readData(function(data){
21
34
  if (data === null){
22
35
  completion.call(target, new Error("Failed to read file data"));
23
36
  return;
24
37
  }
25
- this.addDataForFilename(data, file.name, completion, target);
38
+ var error = null;
39
+ try{
40
+ this.addDataForFilename(data, directory + file.name, attributes);
41
+ }catch (e){
42
+ error = e;
43
+ }
44
+ completion.call(target, error);
26
45
  }, this);
27
46
  return completion.promise;
28
47
  },
29
48
 
30
- addDataForFilename: function(data, name, completion, target){
31
- if (!completion){
32
- completion = Promise.completion(Promise.resolveNull);
33
- }
34
- var error = null;
35
- try{
36
- var encodedName = name.utf8();
37
- var header = JSData.initWithLength(30 + encodedName.length);
38
- var dataView = header.dataView();
39
- var crc = JSCRC32(data);
40
- dataView.setUint32(0, 0x04034b50, true);
41
- dataView.setUint16(4, 20, true);
42
- dataView.setUint16(6, 0, true);
43
- dataView.setUint16(8, 0, true); // no compression
44
- dataView.setUint16(10, 0, true); // date
45
- dataView.setUint16(12, 0, true); // date
46
- dataView.setUint32(14, crc, true);
47
- dataView.setUint32(18, data.length, true);
48
- dataView.setUint32(22, data.length, true);
49
- dataView.setUint16(26, encodedName.length, true);
50
- dataView.setUint16(28, 0, true);
51
- encodedName.copyTo(header, 30);
52
- this.directory.push({
53
- name: name,
54
- offset: this.offset,
55
- header: header
56
- });
57
- this.addChunk(header);
58
- this.addChunk(data);
59
- }catch (e){
60
- error = e;
49
+ addDataForFilename: function(data, name, attributes, deflate){
50
+ var encodedName = name.utf8();
51
+ var header = JSData.initWithLength(30 + encodedName.length);
52
+ var dataView = header.dataView();
53
+ var crc = JSCRC32(data);
54
+ var dateComponents = JSCalendar.gregorian.componentsFromDate(JSCalendar.Unit.day | JSCalendar.Unit.month | JSCalendar.Unit.year | JSCalendar.Unit.hour | JSCalendar.Unit.minute | JSCalendar.Unit.second, attributes.updated !== undefined ? JSDate.initWithTimeIntervalSince1970(attributes.updated) : JSDate.now, JSTimeZone.local || JSTimeZone.utc);
55
+ var dosDate = ((dateComponents.year - 1980) << 9) | ((dateComponents.month) << 5) | (dateComponents.day);
56
+ var dosTime = ((dateComponents.hour) << 11) | ((dateComponents.minute) << 5) | (Math.floor(dateComponents.second / 2));
57
+ var storedData = data;
58
+ var method = 0;
59
+ if (deflate){
60
+ var stream = DeflateStream();
61
+ stream.input = data;
62
+ stream.output = JSData.initWithLength(data.length);
63
+ stream.deflate(true);
64
+ if (stream.state === DeflateStream.State.done){
65
+ storedData = stream.output.subdataInRange(JSRange(0, stream.outputOffset));
66
+ method = 8;
67
+ }
61
68
  }
62
- completion.call(target, error);
63
- return completion.promise;
69
+ // Local File Header (APPNOTE.txt 4.3.7)
70
+ // Everything is little-endian
71
+ dataView.setUint32(0, 0x04034b50, true); // signtature
72
+ dataView.setUint16(4, 0x0014, true); // version needed to extract (2.0)
73
+ dataView.setUint16(6, 0, true); // general purpose flags
74
+ dataView.setUint16(8, method, true); // compression method (0=none, 8=deflate)
75
+ dataView.setUint16(10, dosTime, true); // last modified time (MS-DOS)
76
+ dataView.setUint16(12, dosDate, true); // last modified date (MS-DOS)
77
+ dataView.setUint32(14, crc, true); // crc-32 (magic number 0xdebb20e3, preconditioned to 0xffffffff, post-conditioned by one's compliment)
78
+ dataView.setUint32(18, storedData.length, true); // compressed size
79
+ dataView.setUint32(22, data.length, true); // uncompressed size
80
+ dataView.setUint16(26, encodedName.length, true); // name length
81
+ dataView.setUint16(28, 0, true); // extra field length
82
+ encodedName.copyTo(header, 30); // name (NOT null-terminated)
83
+ this.directory.push({
84
+ name: name,
85
+ offset: this.offset,
86
+ header: header
87
+ });
88
+ this.chunks.push(header);
89
+ this.chunks.push(storedData);
90
+ this.offset += header.length + storedData.length;
91
+ this._data = null;
64
92
  },
65
93
 
66
- close: function(completion, target){
67
- if (!completion){
68
- completion = Promise.completion(Promise.resolveNull);
69
- }
70
- var error = null;
71
- try{
94
+ directory: null,
95
+
96
+ chunks: null,
97
+
98
+ data: JSReadOnlyProperty("_data", null),
99
+
100
+ getData: function(){
101
+ if (this._data === null){
102
+ var chunks = JSCopy(this.chunks);
72
103
  var i, l;
73
104
  var centralDirectoryOffset = this.offset;
74
105
  var centralDirectorySize = 0;
@@ -77,49 +108,39 @@ JSClass("JSZip", JSObject, {
77
108
  var dataView;
78
109
  var file;
79
110
  for (i = 0, l = this.directory.length; i < l; ++i){
111
+ // Central Directory File Header (APPNOTE.txt 4.3.12)
112
+ // Everything is little-endian
80
113
  file = this.directory[i];
81
114
  header = JSData.initWithLength(46 + (file.header.length - 30));
82
115
  dataView = header.dataView();
83
- dataView.setUint32(0, 0x02014b50, true);
84
- dataView.setUint16(4, 0, true);
85
- file.header.subdataInRange(JSRange(4, 26)).copyTo(header, 6);
86
- dataView.setUint16(32, 0, true);
87
- dataView.setUint16(34, 0, true);
88
- dataView.setUint16(36, 0, true);
89
- dataView.setUint32(38, 0, true);
90
- dataView.setUint32(42, file.offset, true);
91
- file.header.subdataInRange(JSRange(30, file.header.length - 30)).copyTo(header, 46);
116
+ dataView.setUint32(0, 0x02014b50, true); // signature
117
+ dataView.setUint16(4, 0x0314, true); // version made by (UNIX=3, 2.0)
118
+ file.header.subdataInRange(JSRange(4, 26)).copyTo(header, 6); // copy from local header
119
+ dataView.setUint16(32, 0, true); // commenet length
120
+ dataView.setUint16(34, 0, true); // disk number start
121
+ dataView.setUint16(36, 0, true); // internal file attributes
122
+ dataView.setUint32(38, 0, true); // external file attributes
123
+ dataView.setUint32(42, file.offset, true); // offset of local header relative to first disk
124
+ file.header.subdataInRange(JSRange(30, file.header.length - 30)).copyTo(header, 46); // name (ALWAYS forward slashes for directories)
92
125
  centralDirectorySize += header.length;
93
- this.addChunk(header);
126
+ chunks.push(header);
94
127
  }
128
+ // End of Central Directory Record (APPNOTE.txt 4.3.16)
129
+ // everything is little-endian
95
130
  var end = JSData.initWithLength(22);
96
131
  dataView = end.dataView();
97
- dataView.setUint32(0, 0x06054b50, true);
98
- dataView.setUint16(4, 0, true);
99
- dataView.setUint16(6, 0, true);
100
- dataView.setUint16(8, this.directory.length, true);
101
- dataView.setUint16(10, this.directory.length, true);
102
- dataView.setUint32(12, centralDirectorySize, true);
103
- dataView.setUint32(16, centralDirectoryOffset, true);
104
- dataView.setUint16(20, 0, true);
105
- this.addChunk(end);
106
- this._data = JSData.initWithChunks(this.chunks);
107
- }catch (e){
108
- error = e;
132
+ dataView.setUint32(0, 0x06054b50, true); // signature
133
+ dataView.setUint16(4, 0, true); // number of this disk
134
+ dataView.setUint16(6, 0, true); // number of the disk with the start of central directory
135
+ dataView.setUint16(8, this.directory.length, true); // number of central directory items on this disk
136
+ dataView.setUint16(10, this.directory.length, true); // number of central directory items total
137
+ dataView.setUint32(12, centralDirectorySize, true); // size of central directory
138
+ dataView.setUint32(16, centralDirectoryOffset, true); // offset to central directory with respect to starting disk
139
+ dataView.setUint16(20, 0, true); // zip file comemnt length
140
+ chunks.push(end);
141
+ this._data = JSData.initWithChunks(chunks);
109
142
  }
110
- completion.call(target, error);
111
- return completion.promise;
112
- },
113
-
114
- addChunk: function(chunk){
115
- this.chunks.push(chunk);
116
- this.offset += chunk.length;
117
- },
118
-
119
- directory: null,
120
-
121
- chunks: null,
122
-
123
- data: JSReadOnlyProperty("_data", null),
143
+ return this._data;
144
+ }
124
145
 
125
146
  });
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.ImageKit
3
- JSBundleVersion: 2022.24.0
3
+ JSBundleVersion: 2022.26.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSBundleEnvironments:
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.MediaKit
3
- JSBundleVersion: 2022.24.0
3
+ JSBundleVersion: 2022.26.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSBundleEnvironments:
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.MediaKitUI
3
- JSBundleVersion: 2022.24.0
3
+ JSBundleVersion: 2022.26.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSBundleEnvironments:
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.NotificationKit
3
- JSBundleVersion: 2022.24.0
3
+ JSBundleVersion: 2022.26.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2021 Breakside Inc.
6
6
  JSBundleEnvironments:
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.PDFKit
3
- JSBundleVersion: 2022.24.0
3
+ JSBundleVersion: 2022.26.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSLicenseNotice: |
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.QRKit
3
- JSBundleVersion: 2022.24.0
3
+ JSBundleVersion: 2022.26.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSLicenseNotice: |
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.SearchKit
3
- JSBundleVersion: 2022.24.0
3
+ JSBundleVersion: 2022.26.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSLicenseNotice: |
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.SecurityKit
3
- JSBundleVersion: 2022.24.0
3
+ JSBundleVersion: 2022.26.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSBundleEnvironments:
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.ServerKit
3
- JSBundleVersion: 2022.24.0
3
+ JSBundleVersion: 2022.26.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSBundleEnvironments:
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.ServerKitTesting
3
- JSBundleVersion: 2022.24.0
3
+ JSBundleVersion: 2022.26.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSLicenseNotice: |
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.TestKit
3
- JSBundleVersion: 2022.24.0
3
+ JSBundleVersion: 2022.26.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSLicenseNotice: |
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.UIKit
3
- JSBundleVersion: 2022.24.0
3
+ JSBundleVersion: 2022.26.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSBundleEnvironments:
@@ -88,7 +88,7 @@ JSClass('UIBasicAnimation', UIPropertyAnimation, {
88
88
  var progress = this.timingFunction(this._state.currentPercentage);
89
89
  this.isComplete = this._state.isComplete();
90
90
  this._updateContext[this._updateProperty] = this.interpolation(this._fromValue, this._toValue, progress);
91
- if (this._updateContext === this._layer.presentation && this._updateProperty === "bounds"){
91
+ if (this._updateContext === this._layer.presentation && (this._updateProperty === "bounds" || this._updateProperty === "imageFrame")){
92
92
  this._layer.setNeedsDisplay();
93
93
  }
94
94
  },
@@ -85,7 +85,16 @@ JSClass("UICursor", JSObject, {
85
85
 
86
86
  pop: function(){
87
87
  UICursor.pop();
88
+ },
89
+
90
+ set: function(){
91
+ UICursor.set(this);
92
+ },
93
+
94
+ unset: function(){
95
+ UICursor.set(UICursor.systemDefault);
88
96
  }
97
+
89
98
  });
90
99
 
91
100
  UICursor.hide = function(){
@@ -107,17 +116,22 @@ UICursor.unhide = function(){
107
116
 
108
117
  UICursor.push = function(cursor){
109
118
  UICursor._stack.push(cursor);
110
- UICursor.show();
119
+ UICursor.update();
111
120
  };
112
121
 
113
122
  UICursor.pop = function(){
114
123
  if (UICursor._stack.length > 1){
115
124
  UICursor._stack.pop();
116
- UICursor.show();
125
+ UICursor.update();
117
126
  }
118
127
  };
119
128
 
120
- UICursor.show = function(){
129
+ UICursor.set = function(cursor){
130
+ UICursor._stack[0] = cursor;
131
+ UICursor.update();
132
+ };
133
+
134
+ UICursor.update = function(){
121
135
  if (this._hideCount === 0){
122
136
  UIApplication.shared.windowServer.setCursor(UICursor.currentCursor);
123
137
  }
@@ -160,4 +174,5 @@ UICursor.dragCopy = UICursor.initWithSystemIdentifier(UICursor.SystemIdentifier.
160
174
  UICursor.crosshair = UICursor.initWithSystemIdentifier(UICursor.SystemIdentifier.crosshair);
161
175
 
162
176
  UICursor._hideCount = 0;
163
- UICursor._stack = [UICursor.arrow];
177
+ UICursor.systemDefault = UICursor.arrow;
178
+ UICursor._stack = [UICursor.systemDefault];
@@ -29,6 +29,7 @@ JSClass("UIHTMLApplication", UIApplication, {
29
29
  initWithBundle: function(bundle, windowServer){
30
30
  UIHTMLApplication.$super.initWithBundle.call(this, bundle, windowServer);
31
31
  this.domWindow = this.windowServer.domWindow;
32
+ this.rememberStateInPath = this.bundle.info.UIHTMLUseURLPathForState === true;
32
33
  },
33
34
 
34
35
  setup: function(completion, target){
@@ -69,7 +70,7 @@ JSClass("UIHTMLApplication", UIApplication, {
69
70
  if (options === undefined){
70
71
  options = {};
71
72
  }
72
- if (options.replacingApplication){
73
+ if (options.replacingApplication || url.scheme === "mailto" || url.scheme === "tel"){
73
74
  var application = this;
74
75
  var open = function(){
75
76
  application.domWindow.location.href = url.encodedString;
@@ -93,17 +94,15 @@ JSClass("UIHTMLApplication", UIApplication, {
93
94
  this.stop(reload);
94
95
  },
95
96
 
96
- baseURL: JSReadOnlyProperty('_baseURL'),
97
+ baseURL: JSReadOnlyProperty('_baseURL', null),
98
+ rememberStateInPath: false,
97
99
 
98
100
  setState: function(state){
99
- UIHTMLApplication.$super.setState.call(this, state);
100
- var url = JSURL.initWithURL(this.baseURL);
101
- if (state.path !== "/"){
102
- url.fragment = state.path;
103
- }
104
- if (!this._isHandlingHashChange){
105
- var href = url.encodedString;
106
- if (href != this.domWindow.location.href){
101
+ if (!state.isEqual(this.state)){
102
+ UIHTMLApplication.$super.setState.call(this, state);
103
+ if (!this._isHandlingBrowserStateChange){
104
+ var url = this.urlForState(state);
105
+ var href = url.encodedString;
107
106
  this.domWindow.history.pushState(null, null, url.encodedString);
108
107
  }
109
108
  }
@@ -111,11 +110,20 @@ JSClass("UIHTMLApplication", UIApplication, {
111
110
 
112
111
  setStateReplacingHTMLState: function(state){
113
112
  UIHTMLApplication.$super.setState.call(this, state);
113
+ var url = this.urlForState(state);
114
+ this.domWindow.history.replaceState(null, null, url.encodedString);
115
+ },
116
+
117
+ urlForState: function(state){
114
118
  var url = JSURL.initWithURL(this.baseURL);
115
- if (state.path !== "/"){
116
- url.fragment = state.path;
119
+ if (state.pathComponents.length > 1){
120
+ if (this.rememberStateInPath){
121
+ url.appendPathComponents(state.pathComponents.slice(1));
122
+ }else{
123
+ url.fragment = state.path;
124
+ }
117
125
  }
118
- this.domWindow.history.replaceState(null, null, url.encodedString);
126
+ return url;
119
127
  },
120
128
 
121
129
  addEventListeners: function(){
@@ -123,6 +131,7 @@ JSClass("UIHTMLApplication", UIApplication, {
123
131
  this.domWindow.addEventListener("unhandledrejection", this);
124
132
  this.domWindow.addEventListener("beforeunload", this);
125
133
  this.domWindow.addEventListener("hashchange", this);
134
+ this.domWindow.addEventListener("popstate", this);
126
135
  },
127
136
 
128
137
  handleEvent: function(e){
@@ -160,7 +169,10 @@ JSClass("UIHTMLApplication", UIApplication, {
160
169
  },
161
170
 
162
171
  _event_hashchange: function(e){
163
- this._isHandlingHashChange = true;
172
+ if (this.rememberStateInPath){
173
+ return;
174
+ }
175
+ this._isHandlingBrowserStateChange = true;
164
176
  try{
165
177
  var requestedURL = JSURL.initWithString(this.domWindow.location.href);
166
178
  var requestedFragment = requestedURL.fragment;
@@ -185,7 +197,33 @@ JSClass("UIHTMLApplication", UIApplication, {
185
197
  }
186
198
  this.setStateReplacingHTMLState(state);
187
199
  }finally{
188
- this._isHandlingHashChange = false;
200
+ this._isHandlingBrowserStateChange = false;
201
+ }
202
+ },
203
+
204
+ _event_popstate: function(e){
205
+ if (!this.rememberStateInPath){
206
+ return;
207
+ }
208
+ this._isHandlingBrowserStateChange = true;
209
+ try{
210
+ var requestedURL = JSURL.initWithString(this.domWindow.location.href);
211
+ var relativeURL = JSURL.initWithString(requestedURL.encodedStringRelativeTo(this._baseURL));
212
+ var requestedState = UIState.initWithPath(relativeURL.path);
213
+ var state = null;
214
+ if (this.delegate && this.delegate.applicationDidRequestState){
215
+ state = this.delegate.applicationDidRequestState(this, requestedState);
216
+ }
217
+ if (state === null || state === undefined){
218
+ state = this.state;
219
+ }
220
+ if (state.isEqual(requestedState)){
221
+ UIHTMLApplication.$super.setState.call(this, state);
222
+ }else{
223
+ this.setStateReplacingHTMLState(state);
224
+ }
225
+ }finally{
226
+ this._isHandlingBrowserStateChange = false;
189
227
  }
190
228
  },
191
229
 
@@ -79,7 +79,6 @@ JSClass("UIHTMLDisplayServerCanvasContext", UIHTMLDisplayServerContext, {
79
79
  // MARK: - Destroying a Context
80
80
 
81
81
  destroy: function(){
82
- this.trackingElement = null;
83
82
  this.style = null;
84
83
  UIHTMLDisplayServerCanvasContext.$super.destroy.call(this);
85
84
  },
@@ -140,11 +139,7 @@ JSClass("UIHTMLDisplayServerCanvasContext", UIHTMLDisplayServerContext, {
140
139
  // instead of a separate element. Needs investigation. Browser support? Behavior?
141
140
  this.borderElement = this.element.ownerDocument.createElement('div');
142
141
  this.borderElement.setAttribute("role", "none presentation");
143
- if (this.trackingElement !== null){
144
- this.element.insertBefore(this.borderElement, this.trackingElement);
145
- }else{
146
- this.element.appendChild(this.borderElement);
147
- }
142
+ this.element.appendChild(this.borderElement);
148
143
  this.borderElement.style.position = 'absolute';
149
144
  this.borderElement.style.top = '0';
150
145
  this.borderElement.style.left = '0';
@@ -465,48 +460,7 @@ JSClass("UIHTMLDisplayServerCanvasContext", UIHTMLDisplayServerContext, {
465
460
  },
466
461
 
467
462
  // ----------------------------------------------------------------------
468
- // MARK: - Tracking
469
-
470
- trackingElement: null,
471
- trackingListener: null,
472
-
473
- startMouseTracking: function(trackingType, listener, layer){
474
- if (this.trackingElement === null){
475
- this.trackingElement = this.element.ownerDocument.createElement('div');
476
- this.trackingElement.setAttribute("role", "none presentation");
477
- this.trackingElement.style.position = 'absolute';
478
- this.trackingElement.style.top = '0';
479
- this.trackingElement.style.left = '0';
480
- this.trackingElement.style.bottom = '0';
481
- this.trackingElement.style.right = '0';
482
- this.trackingElement.dataset.tag = "tracking";
483
- this.element.appendChild(this.trackingElement);
484
- }else if (this.trackingListener !== null){
485
- this.trackingElement.removeEventListener('mouseenter', this.trackingListener);
486
- this.trackingElement.removeEventListener('mouseleave', this.trackingListener);
487
- this.trackingElement.removeEventListener('mousemove', this.trackingListener);
488
- }
489
- this.trackingListener = listener;
490
- if (trackingType & UIView.MouseTracking.enterAndExit){
491
- this.trackingElement.addEventListener('mouseenter', this.trackingListener);
492
- this.trackingElement.addEventListener('mouseleave', this.trackingListener);
493
- }
494
- if (trackingType & UIView.MouseTracking.move){
495
- this.trackingElement.addEventListener('mousemove', this.trackingListener);
496
- }
497
- },
498
-
499
- stopMouseTracking: function(){
500
- if (this.trackingElement === null || this.trackingListener === null){
501
- return;
502
- }
503
- this.trackingElement.removeEventListener('mouseenter', this.trackingListener);
504
- this.trackingElement.removeEventListener('mouseleave', this.trackingListener);
505
- this.trackingElement.removeEventListener('mousemove', this.trackingListener);
506
- this.trackingElement.parentNode.removeChild(this.trackingElement);
507
- this.trackingElement = null;
508
- this.trackingListener = null;
509
- },
463
+ // MARK: - Cursor
510
464
 
511
465
  setCursor: function(cursor){
512
466
  this.element.style.cursor = '';
@@ -43,12 +43,6 @@ JSClass("UIHTMLDisplayServerContext", JSContext, {
43
43
  layerDidChangeProperty: function(layer, property){
44
44
  },
45
45
 
46
- startMouseTracking: function(trackingType, listener){
47
- },
48
-
49
- stopMouseTracking: function(){
50
- },
51
-
52
46
  addExternalElementInRect: function(element, rect){
53
47
  },
54
48