plist 0.2.0 → 0.2.1
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.
- package/LICENSE +22 -0
- package/lib/plist.js +130 -93
- package/package.json +1 -1
- package/tests/airplay.xml +38 -0
- package/tests/test.js +19 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2010-2011- Nathan Rajlich
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person
|
|
4
|
+
obtaining a copy of this software and associated documentation
|
|
5
|
+
files (the "Software"), to deal in the Software without
|
|
6
|
+
restriction, including without limitation the rights to use,
|
|
7
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
copies of the Software, and to permit persons to whom the
|
|
9
|
+
Software is furnished to do so, subject to the following
|
|
10
|
+
conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be
|
|
13
|
+
included in all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
17
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
19
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
20
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
21
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
package/lib/plist.js
CHANGED
|
@@ -1,177 +1,214 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
var
|
|
1
|
+
;(function (exports, sax) {
|
|
2
|
+
//Checks if running in a non-browser environment
|
|
3
|
+
var inNode = typeof window === 'undefined' ? true : false;
|
|
4
4
|
|
|
5
|
-
function Parser() {
|
|
5
|
+
function Parser() {
|
|
6
6
|
sax.SAXParser.call(this, false, { lowercasetags: true, trim: false });
|
|
7
|
-
}
|
|
7
|
+
}
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
var inherits = null;
|
|
10
|
+
if (inNode) {
|
|
11
|
+
var fs = require('fs');
|
|
12
|
+
inherits = require('util').inherits; //use node provided function
|
|
13
|
+
} else { //use in browser
|
|
14
|
+
if ("create" in Object) {
|
|
15
|
+
inherits = function(ctor, superCtor) {
|
|
16
|
+
ctor.super_ = superCtor;
|
|
17
|
+
ctor.prototype = Object.create(superCtor.prototype, {
|
|
18
|
+
constructor: {
|
|
19
|
+
value: ctor,
|
|
20
|
+
enumerable: false,
|
|
21
|
+
writable: true,
|
|
22
|
+
configurable: true
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
} else {
|
|
27
|
+
var klass = function() {};
|
|
28
|
+
inherits = function(ctor, superCtor) {
|
|
29
|
+
klass.prototype = superCtor.prototype;
|
|
30
|
+
ctor.prototype = new klass;
|
|
31
|
+
ctor.prototype['constructor'] = ctor;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
inherits(Parser, sax.SAXParser); //inherit from sax (browser-style or node-style)
|
|
10
36
|
|
|
11
|
-
Parser.prototype.getInteger = function (string) {
|
|
37
|
+
Parser.prototype.getInteger = function (string) {
|
|
12
38
|
this.value = parseInt(string, 10);
|
|
13
|
-
}
|
|
14
|
-
Parser.prototype.
|
|
39
|
+
}
|
|
40
|
+
Parser.prototype.getReal = function (string) {
|
|
41
|
+
this.value = parseFloat(string);
|
|
42
|
+
}
|
|
43
|
+
Parser.prototype.getString = function (string) {
|
|
15
44
|
this.value += string;
|
|
16
|
-
}
|
|
17
|
-
Parser.prototype.getData = function(string) {
|
|
45
|
+
}
|
|
46
|
+
Parser.prototype.getData = function(string) {
|
|
18
47
|
// todo: parse base64 encoded data
|
|
19
48
|
this.value += string;
|
|
20
|
-
}
|
|
21
|
-
Parser.prototype.getDate = function (string) {
|
|
49
|
+
}
|
|
50
|
+
Parser.prototype.getDate = function (string) {
|
|
22
51
|
this.value = new Date(string);
|
|
23
|
-
}
|
|
52
|
+
}
|
|
24
53
|
|
|
25
|
-
Parser.prototype.addToDict = function (value) {
|
|
54
|
+
Parser.prototype.addToDict = function (value) {
|
|
26
55
|
this.dict[this.key] = value;
|
|
27
|
-
}
|
|
28
|
-
Parser.prototype.addToArray = function (value) {
|
|
56
|
+
}
|
|
57
|
+
Parser.prototype.addToArray = function (value) {
|
|
29
58
|
this.array.push(value);
|
|
30
|
-
}
|
|
59
|
+
}
|
|
31
60
|
|
|
32
|
-
Parser.prototype.onopentag = function (tag) {
|
|
61
|
+
Parser.prototype.onopentag = function (tag) {
|
|
33
62
|
switch (tag.name) {
|
|
34
|
-
|
|
63
|
+
case 'dict':
|
|
35
64
|
this.stack.push(this.context);
|
|
36
65
|
this.context = {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
66
|
+
value: function() {
|
|
67
|
+
return this.dict;
|
|
68
|
+
},
|
|
69
|
+
dict: {},
|
|
70
|
+
setKey: function(key) {
|
|
71
|
+
this.key = key;
|
|
72
|
+
},
|
|
73
|
+
setValue: function(value) {
|
|
74
|
+
this.dict[this.key] = value;
|
|
75
|
+
}
|
|
47
76
|
}
|
|
48
77
|
break;
|
|
49
|
-
|
|
50
|
-
|
|
78
|
+
case 'plist':
|
|
79
|
+
case 'array':
|
|
51
80
|
this.stack.push(this.context);
|
|
52
81
|
this.context = {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
82
|
+
value: function() {
|
|
83
|
+
return this.array;
|
|
84
|
+
},
|
|
85
|
+
array: [],
|
|
86
|
+
setKey: function(key) {
|
|
87
|
+
console.log('unexpected <key> element in array');
|
|
88
|
+
},
|
|
89
|
+
setValue: function(value) {
|
|
90
|
+
this.array.push(value);
|
|
91
|
+
}
|
|
63
92
|
}
|
|
64
93
|
break;
|
|
65
|
-
|
|
94
|
+
case 'key':
|
|
66
95
|
this.ontext = function (text) {
|
|
67
|
-
|
|
96
|
+
this.context.setKey(text);
|
|
68
97
|
}
|
|
69
98
|
break;
|
|
70
|
-
|
|
99
|
+
case 'integer':
|
|
71
100
|
this.ontext = this.getInteger;
|
|
72
101
|
break;
|
|
73
|
-
|
|
102
|
+
case 'real':
|
|
103
|
+
this.ontext = this.getReal;
|
|
104
|
+
break;
|
|
105
|
+
case 'string':
|
|
74
106
|
this.value = '';
|
|
75
107
|
this.ontext = this.getString;
|
|
76
108
|
this.oncdata = this.getString;
|
|
77
109
|
break;
|
|
78
|
-
|
|
110
|
+
case 'data':
|
|
79
111
|
this.value = '';
|
|
80
112
|
this.ontext = this.getData;
|
|
81
113
|
this.oncdata = this.getData;
|
|
82
114
|
break;
|
|
83
|
-
|
|
115
|
+
case 'true':
|
|
84
116
|
this.value = true;
|
|
85
117
|
break;
|
|
86
|
-
|
|
118
|
+
case 'false':
|
|
87
119
|
this.value = false;
|
|
88
120
|
break;
|
|
89
|
-
|
|
121
|
+
case 'date':
|
|
90
122
|
this.ontext = this.getDate;
|
|
91
123
|
break;
|
|
92
|
-
|
|
124
|
+
default:
|
|
93
125
|
console.log('ignored tag', tag.name);
|
|
94
126
|
break;
|
|
95
127
|
}
|
|
96
|
-
}
|
|
97
|
-
Parser.prototype.onclosetag = function (tag) {
|
|
128
|
+
}
|
|
129
|
+
Parser.prototype.onclosetag = function (tag) {
|
|
98
130
|
var value;
|
|
99
131
|
switch (tag) {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
132
|
+
case 'dict':
|
|
133
|
+
case 'array':
|
|
134
|
+
case 'plist':
|
|
103
135
|
var value = this.context.value();
|
|
104
136
|
this.context = this.stack.pop();
|
|
105
137
|
this.context.setValue(value);
|
|
106
138
|
break;
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
139
|
+
case 'true':
|
|
140
|
+
case 'false':
|
|
141
|
+
case 'string':
|
|
142
|
+
case 'integer':
|
|
143
|
+
case 'real':
|
|
144
|
+
case 'date':
|
|
145
|
+
case 'data':
|
|
113
146
|
this.context.setValue(this.value);
|
|
114
147
|
break;
|
|
115
|
-
|
|
148
|
+
case 'key':
|
|
116
149
|
break;
|
|
117
|
-
|
|
150
|
+
default:
|
|
118
151
|
console.log('closing', tag, 'tag ignored');
|
|
119
152
|
}
|
|
120
153
|
this.oncdata = this.ontext = this.checkWhitespace;
|
|
121
|
-
}
|
|
122
|
-
Parser.prototype.checkWhitespace = function (data) {
|
|
154
|
+
}
|
|
155
|
+
Parser.prototype.checkWhitespace = function (data) {
|
|
123
156
|
if (!data.match(/^[ \t\r\n]*$/)) {
|
|
124
|
-
|
|
157
|
+
console.log('unexpected non-whitespace data', data);
|
|
125
158
|
}
|
|
126
|
-
}
|
|
127
|
-
Parser.prototype.oncomment = function (comment) {
|
|
128
|
-
}
|
|
129
|
-
Parser.prototype.onerror = function (error) {
|
|
159
|
+
}
|
|
160
|
+
Parser.prototype.oncomment = function (comment) {
|
|
161
|
+
}
|
|
162
|
+
Parser.prototype.onerror = function (error) {
|
|
130
163
|
console.log('sax parser error:', error);
|
|
131
164
|
throw error;
|
|
132
|
-
}
|
|
165
|
+
}
|
|
133
166
|
|
|
134
|
-
Parser.prototype.parseFile = function (xmlfile, callback) {
|
|
167
|
+
if (inNode) Parser.prototype.parseFile = function (xmlfile, callback) { //browsers aren't capable of opening files, instead use AJAX
|
|
135
168
|
var parser = this;
|
|
136
169
|
parser.stack = [ ];
|
|
137
170
|
parser.context = {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
171
|
+
callback: callback,
|
|
172
|
+
value: function() {},
|
|
173
|
+
setKey: function(key) {},
|
|
174
|
+
setValue: function(value) {
|
|
175
|
+
callback(null, value);
|
|
176
|
+
},
|
|
144
177
|
}
|
|
145
178
|
var rs = fs.createReadStream(xmlfile, {
|
|
146
179
|
encoding: 'utf8'
|
|
147
180
|
});
|
|
148
181
|
rs.on('data', function(data) { parser.write(data); });
|
|
149
182
|
rs.on('end', function() { parser.close(); });
|
|
150
|
-
}
|
|
183
|
+
}
|
|
151
184
|
|
|
152
|
-
Parser.prototype.parseString = function (xml, callback) {
|
|
185
|
+
Parser.prototype.parseString = function (xml, callback) {
|
|
153
186
|
var parser = this;
|
|
154
187
|
parser.stack = [ ];
|
|
155
188
|
parser.context = {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
189
|
+
callback: callback,
|
|
190
|
+
value: function() {},
|
|
191
|
+
setKey: function(key) {},
|
|
192
|
+
setValue: function(value) {
|
|
193
|
+
this.callback(null, value);
|
|
194
|
+
},
|
|
162
195
|
};
|
|
163
196
|
parser.write(xml);
|
|
164
197
|
parser.close();
|
|
165
|
-
}
|
|
198
|
+
}
|
|
166
199
|
|
|
167
|
-
exports.Parser = Parser;
|
|
200
|
+
exports.Parser = Parser;
|
|
168
201
|
|
|
169
|
-
exports.parseString = function (xml, callback) {
|
|
202
|
+
exports.parseString = function (xml, callback) {
|
|
170
203
|
var parser = new Parser();
|
|
171
204
|
parser.parseString(xml, callback);
|
|
172
|
-
}
|
|
205
|
+
}
|
|
173
206
|
|
|
174
|
-
exports.parseFile = function (filename, callback) {
|
|
207
|
+
if (inNode) exports.parseFile = function (filename, callback) { //Do not expose no created method
|
|
175
208
|
var parser = new Parser();
|
|
176
209
|
parser.parseFile(filename, callback);
|
|
177
|
-
}
|
|
210
|
+
}
|
|
211
|
+
})(typeof exports === 'undefined' ? plist = {} : exports, typeof window === "undefined" ? require('sax') : sax)
|
|
212
|
+
//the above line checks for exports (defined in node) and uses it, or creates a global variable and exports to that.
|
|
213
|
+
//also, if in node, require sax node-style, in browser the developer must use a <script> tag to import sax
|
|
214
|
+
// TODO: Implement detection of 'sax' in the Browser environment
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "plist",
|
|
3
3
|
"description": "Mac OS X Plist parser for NodeJS. Convert a Plist file or string into a native JS object",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.1",
|
|
5
5
|
"author": "Nathan Rajlich <nathan@tootallnate.net>",
|
|
6
6
|
"contributors": [ "Hans Huebner <hans.huebner@gmail.com>" ],
|
|
7
7
|
"repository": {
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<key>duration</key>
|
|
6
|
+
<real>5555.0495000000001</real>
|
|
7
|
+
<key>loadedTimeRanges</key>
|
|
8
|
+
<array>
|
|
9
|
+
<dict>
|
|
10
|
+
<key>duration</key>
|
|
11
|
+
<real>5555.0495000000001</real>
|
|
12
|
+
<key>start</key>
|
|
13
|
+
<real>0.0</real>
|
|
14
|
+
</dict>
|
|
15
|
+
</array>
|
|
16
|
+
<key>playbackBufferEmpty</key>
|
|
17
|
+
<true/>
|
|
18
|
+
<key>playbackBufferFull</key>
|
|
19
|
+
<false/>
|
|
20
|
+
<key>playbackLikelyToKeepUp</key>
|
|
21
|
+
<true/>
|
|
22
|
+
<key>position</key>
|
|
23
|
+
<real>4.6269989039999997</real>
|
|
24
|
+
<key>rate</key>
|
|
25
|
+
<real>1</real>
|
|
26
|
+
<key>readyToPlay</key>
|
|
27
|
+
<true/>
|
|
28
|
+
<key>seekableTimeRanges</key>
|
|
29
|
+
<array>
|
|
30
|
+
<dict>
|
|
31
|
+
<key>duration</key>
|
|
32
|
+
<real>5555.0495000000001</real>
|
|
33
|
+
<key>start</key>
|
|
34
|
+
<real>0.0</real>
|
|
35
|
+
</dict>
|
|
36
|
+
</array>
|
|
37
|
+
</dict>
|
|
38
|
+
</plist>
|
package/tests/test.js
CHANGED
|
@@ -37,3 +37,22 @@ plist.parseFile(file2, function(err, dicts) {
|
|
|
37
37
|
assert.equal(dict['PopupMenu'][2]['Key'], "\n \n #import <Cocoa/Cocoa.h>\n\n#import <MacRuby/MacRuby.h>\n\nint main(int argc, char *argv[])\n{\n return macruby_main(\"rb_main.rb\", argc, argv);\n}\n\n");
|
|
38
38
|
|
|
39
39
|
});
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
// Third test...
|
|
43
|
+
var file3 = path.join(__dirname, "airplay.xml");
|
|
44
|
+
var startTime3 = new Date();
|
|
45
|
+
|
|
46
|
+
plist.parseFile(file3, function(err, dicts) {
|
|
47
|
+
if (err) {
|
|
48
|
+
throw err;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
var endTime = new Date();
|
|
52
|
+
console.log('Parsed "' + file3 + '" in ' + (endTime - startTime3) + 'ms');
|
|
53
|
+
|
|
54
|
+
var dict = dicts[0];
|
|
55
|
+
assert.equal(dict['duration'], 5555.0495000000001);
|
|
56
|
+
assert.equal(dict['position'], 4.6269989039999997);
|
|
57
|
+
|
|
58
|
+
});
|