Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
xxadev
vscode
提交
0bee43b5
V
vscode
项目概览
xxadev
/
vscode
与 Fork 源项目一致
从无法访问的项目Fork
通知
2
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
V
vscode
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
0bee43b5
编写于
7月 22, 2017
作者:
R
Ramya Achutha Rao
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Emmet Update Image Size command
上级
caa0ef4e
变更
7
隐藏空白更改
内联
并排
Showing
7 changed file
with
551 addition
and
2 deletion
+551
-2
extensions/emmet/package.json
extensions/emmet/package.json
+2
-1
extensions/emmet/src/extension.ts
extensions/emmet/src/extension.ts
+5
-0
extensions/emmet/src/imageSizeHelper.ts
extensions/emmet/src/imageSizeHelper.ts
+119
-0
extensions/emmet/src/locateFile.ts
extensions/emmet/src/locateFile.ts
+91
-0
extensions/emmet/src/typings/EmmetNode.d.ts
extensions/emmet/src/typings/EmmetNode.d.ts
+15
-0
extensions/emmet/src/updateImageSize.ts
extensions/emmet/src/updateImageSize.ts
+304
-0
extensions/emmet/src/util.ts
extensions/emmet/src/util.ts
+15
-1
未找到文件。
extensions/emmet/package.json
浏览文件 @
0bee43b5
...
...
@@ -75,6 +75,7 @@
"@emmetio/css-parser"
:
"^0.3.0"
,
"@emmetio/math-expression"
:
"^0.1.1"
,
"vscode-emmet-helper"
:
"0.0.28"
,
"vscode-languageserver-types"
:
"^3.0.3"
"vscode-languageserver-types"
:
"^3.0.3"
,
"image-size"
:
"^0.5.2"
}
}
\ No newline at end of file
extensions/emmet/src/extension.ts
浏览文件 @
0bee43b5
...
...
@@ -19,6 +19,7 @@ import { evaluateMathExpression } from './evaluateMathExpression';
import
{
incrementDecrement
}
from
'
./incrementDecrement
'
;
import
{
LANGUAGE_MODES
,
getMappingForIncludedLanguages
}
from
'
./util
'
;
import
{
updateExtensionsPath
}
from
'
vscode-emmet-helper
'
;
import
{
updateImageSize
}
from
'
./updateImageSize
'
;
import
*
as
path
from
'
path
'
;
export
function
activate
(
context
:
vscode
.
ExtensionContext
)
{
...
...
@@ -113,6 +114,10 @@ export function activate(context: vscode.ExtensionContext) {
return
incrementDecrement
(
-
10
);
}));
context
.
subscriptions
.
push
(
vscode
.
commands
.
registerCommand
(
'
emmet.updateImageSize
'
,
()
=>
{
return
updateImageSize
();
}));
let
currentExtensionsPath
=
undefined
;
let
resolveUpdateExtensionsPath
=
()
=>
{
let
extensionsPath
=
vscode
.
workspace
.
getConfiguration
(
'
emmet
'
)[
'
extensionsPath
'
];
...
...
extensions/emmet/src/imageSizeHelper.ts
0 → 100644
浏览文件 @
0bee43b5
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// Based on @sergeche's work on the emmet plugin for atom
// TODO: Move to https://github.com/emmetio/image-size
'
use strict
'
;
import
*
as
path
from
'
path
'
;
import
*
as
http
from
'
http
'
;
import
*
as
https
from
'
https
'
;
import
{
parse
as
parseUrl
}
from
'
url
'
;
import
*
as
sizeOf
from
'
image-size
'
;
const
reUrl
=
/^https
?
:/
;
/**
* Get size of given image file. Supports files from local filesystem,
* as well as URLs
* @param {String} file Path to local file or URL
* @return {Promise}
*/
export
function
getImageSize
(
file
)
{
file
=
file
.
replace
(
/^file:
\/\/
/
,
''
);
return
reUrl
.
test
(
file
)
?
getImageSizeFromURL
(
file
)
:
getImageSizeFromFile
(
file
);
}
/**
* Get image size from file on local file system
* @param {String} file
* @return {Promise}
*/
function
getImageSizeFromFile
(
file
)
{
return
new
Promise
((
resolve
,
reject
)
=>
{
const
isDataUrl
=
file
.
match
(
/^data:.+
?
;base64,/
);
if
(
isDataUrl
)
{
// NB should use sync version of `sizeOf()` for buffers
try
{
const
data
=
Buffer
.
from
(
file
.
slice
(
isDataUrl
[
0
].
length
),
'
base64
'
);
return
resolve
(
sizeForFileName
(
''
,
sizeOf
(
data
)));
}
catch
(
err
)
{
return
reject
(
err
);
}
}
sizeOf
(
file
,
(
err
,
size
)
=>
{
if
(
err
)
{
reject
(
err
);
}
else
{
resolve
(
sizeForFileName
(
path
.
basename
(
file
),
size
));
}
});
});
}
/**
* Get image size from given remove URL
* @param {String} url
* @return {Promise}
*/
function
getImageSizeFromURL
(
url
)
{
return
new
Promise
((
resolve
,
reject
)
=>
{
url
=
parseUrl
(
url
);
const
getTransport
=
url
.
protocol
===
'
https:
'
?
https
.
get
:
http
.
get
;
getTransport
(
url
,
resp
=>
{
const
chunks
=
[];
let
bufSize
=
0
;
const
trySize
=
chunks
=>
{
try
{
const
size
=
sizeOf
(
Buffer
.
concat
(
chunks
,
bufSize
));
resp
.
removeListener
(
'
data
'
,
onData
);
resp
.
destroy
();
// no need to read further
resolve
(
sizeForFileName
(
path
.
basename
(
url
.
pathname
),
size
));
}
catch
(
err
)
{
// might not have enough data, skip error
}
};
const
onData
=
chunk
=>
{
bufSize
+=
chunk
.
length
;
chunks
.
push
(
chunk
);
trySize
(
chunks
);
};
resp
.
on
(
'
data
'
,
onData
)
.
on
(
'
end
'
,
()
=>
trySize
(
chunks
))
.
once
(
'
error
'
,
err
=>
{
resp
.
removeListener
(
'
data
'
,
onData
);
reject
(
err
);
});
})
.
once
(
'
error
'
,
reject
);
});
}
/**
* Returns size object for given file name. If file name contains `@Nx` token,
* the final dimentions will be downscaled by N
* @param {String} fileName
* @param {Object} size
* @return {Object}
*/
function
sizeForFileName
(
fileName
,
size
)
{
const
m
=
fileName
.
match
(
/@
(\d
+
)
x
\.
/
);
const
scale
=
m
?
+
m
[
1
]
:
1
;
return
{
realWidth
:
size
.
width
,
realHeight
:
size
.
height
,
width
:
Math
.
floor
(
size
.
width
/
scale
),
height
:
Math
.
floor
(
size
.
height
/
scale
)
};
}
extensions/emmet/src/locateFile.ts
0 → 100644
浏览文件 @
0bee43b5
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// Based on @sergeche's work on the emmet plugin for atom
// TODO: Move to https://github.com/emmetio/file-utils
'
use strict
'
;
import
*
as
path
from
'
path
'
;
import
*
as
fs
from
'
fs
'
;
const
reAbsolute
=
/^
\/
+/
;
/**
* Locates given `filePath` on user’s file system and returns absolute path to it.
* This method expects either URL, or relative/absolute path to resource
* @param {String} basePath Base path to use if filePath is not absoulte
* @param {String} filePath File to locate.
* @return {Promise}
*/
export
function
locateFile
(
base
:
string
,
filePath
:
string
):
Promise
<
string
>
{
if
(
/^
\w
+:/
.
test
(
filePath
))
{
// path with protocol, already absolute
return
Promise
.
resolve
(
filePath
);
}
filePath
=
path
.
normalize
(
filePath
);
return
reAbsolute
.
test
(
filePath
)
?
resolveAbsolute
(
base
,
filePath
)
:
resolveRelative
(
base
,
filePath
);
}
/**
* Resolves relative file path
* @param {TextEditor|String} base
* @param {String} filePath
* @return {Promise}
*/
function
resolveRelative
(
basePath
,
filePath
):
Promise
<
string
>
{
return
tryFile
(
path
.
resolve
(
basePath
,
filePath
));
}
/**
* Resolves absolute file path agaist given editor: tries to find file in every
* parent of editor’s file
* @param {TextEditor|String} base
* @param {String} filePath
* @return {Promise}
*/
function
resolveAbsolute
(
basePath
,
filePath
):
Promise
<
string
>
{
return
new
Promise
((
resolve
,
reject
)
=>
{
filePath
=
filePath
.
replace
(
reAbsolute
,
''
);
const
next
=
ctx
=>
{
tryFile
(
path
.
resolve
(
ctx
,
filePath
))
.
then
(
resolve
,
err
=>
{
const
dir
=
path
.
dirname
(
ctx
);
if
(
!
dir
||
dir
===
ctx
)
{
return
reject
(
`Unable to locate absolute file
${
filePath
}
`
);
}
next
(
dir
);
});
};
next
(
basePath
);
});
}
/**
* Check if given file exists and it’s a file, not directory
* @param {String} file
* @return {Promise}
*/
function
tryFile
(
file
):
Promise
<
string
>
{
return
new
Promise
((
resolve
,
reject
)
=>
{
fs
.
stat
(
file
,
(
err
,
stat
)
=>
{
if
(
err
)
{
return
reject
(
err
);
}
if
(
!
stat
.
isFile
())
{
return
reject
(
new
Error
(
`
${
file
}
is not a file`
));
}
resolve
(
file
);
});
});
}
extensions/emmet/src/typings/EmmetNode.d.ts
浏览文件 @
0bee43b5
...
...
@@ -24,6 +24,16 @@ declare module 'EmmetNode' {
toString
():
string
}
export
interface
CssToken
extends
Token
{
size
:
number
item
(
number
):
any
type
:
string
}
export
interface
HtmlToken
extends
Token
{
value
:
string
}
export
interface
Attribute
extends
Token
{
name
:
string
value
:
Token
...
...
@@ -51,10 +61,15 @@ declare module 'EmmetNode' {
export
interface
Rule
extends
CssNode
{
selectorToken
:
Token
contentStartToken
:
Token
contentEndToken
:
Token
}
export
interface
Property
extends
CssNode
{
valueToken
:
Token
separator
:
Token
parent
:
Rule
terminatorToken
:
Token
}
export
interface
Stylesheet
extends
Node
{
...
...
extensions/emmet/src/updateImageSize.ts
0 → 100644
浏览文件 @
0bee43b5
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// Based on @sergeche's work on the emmet plugin for atom
'
use strict
'
;
import
{
TextEditor
,
Range
,
Position
,
window
}
from
'
vscode
'
;
import
*
as
path
from
'
path
'
;
import
{
getImageSize
}
from
'
./imageSizeHelper
'
;
import
{
isStyleSheet
}
from
'
vscode-emmet-helper
'
;
import
{
parse
,
getNode
,
iterateCSSToken
}
from
'
./util
'
;
import
{
HtmlNode
,
CssToken
,
HtmlToken
,
Attribute
,
Property
}
from
'
EmmetNode
'
;
import
{
locateFile
}
from
'
./locateFile
'
;
/**
* Updates size of context image in given editor
*/
export
function
updateImageSize
()
{
let
editor
=
window
.
activeTextEditor
;
if
(
!
editor
)
{
window
.
showInformationMessage
(
'
No editor is active.
'
);
return
;
}
if
(
!
isStyleSheet
(
editor
.
document
.
languageId
))
{
return
updateImageSizeHTML
(
editor
);
}
else
{
return
updateImageSizeCSS
(
editor
);
}
}
/**
* Updates image size of context tag of HTML model
*/
function
updateImageSizeHTML
(
editor
:
TextEditor
)
{
const
src
=
getImageSrcHTML
(
getImageHTMLNode
(
editor
));
if
(
!
src
)
{
return
Promise
.
reject
(
new
Error
(
'
No valid image source
'
));
}
locateFile
(
path
.
dirname
(
editor
.
document
.
fileName
),
src
)
.
then
(
getImageSize
)
.
then
((
size
:
any
)
=>
{
// since this action is asynchronous, we have to ensure that editor wasn’t
// changed and user didn’t moved caret outside <img> node
const
img
=
getImageHTMLNode
(
editor
);
if
(
getImageSrcHTML
(
img
)
===
src
)
{
updateHTMLTag
(
editor
,
img
,
size
.
width
,
size
.
height
);
}
})
.
catch
(
err
=>
console
.
warn
(
'
Error while updating image size:
'
,
err
));
}
/**
* Updates image size of context rule of stylesheet model
*/
function
updateImageSizeCSS
(
editor
:
TextEditor
)
{
const
src
=
getImageSrcCSS
(
getImageCSSNode
(
editor
),
editor
.
selection
.
active
);
if
(
!
src
)
{
return
Promise
.
reject
(
new
Error
(
'
No valid image source
'
));
}
locateFile
(
path
.
dirname
(
editor
.
document
.
fileName
),
src
)
.
then
(
getImageSize
)
.
then
((
size
:
any
)
=>
{
// since this action is asynchronous, we have to ensure that editor wasn’t
// changed and user didn’t moved caret outside <img> node
const
prop
=
getImageCSSNode
(
editor
);
if
(
getImageSrcCSS
(
prop
,
editor
.
selection
.
active
)
===
src
)
{
updateCSSNode
(
editor
,
prop
,
size
.
width
,
size
.
height
);
}
})
.
catch
(
err
=>
console
.
warn
(
'
Error while updating image size:
'
,
err
));
}
/**
* Returns <img> node under caret in given editor or `null` if such node cannot
* be found
* @param {TextEditor} editor
* @return {HtmlNode}
*/
function
getImageHTMLNode
(
editor
:
TextEditor
):
HtmlNode
{
const
rootNode
=
parse
(
editor
.
document
);
const
node
=
<
HtmlNode
>
getNode
(
rootNode
,
editor
.
selection
.
active
,
true
);
return
node
&&
node
.
name
.
toLowerCase
()
===
'
img
'
?
node
:
null
;
}
/**
* Returns css property under caret in given editor or `null` if such node cannot
* be found
* @param {TextEditor} editor
* @return {Property}
*/
function
getImageCSSNode
(
editor
:
TextEditor
):
Property
{
const
rootNode
=
parse
(
editor
.
document
);
const
node
=
getNode
(
rootNode
,
editor
.
selection
.
active
,
true
);
return
node
&&
node
.
type
===
'
property
'
?
<
Property
>
node
:
null
;
}
/**
* Returns image source from given <img> node
* @param {HtmlNode} node
* @return {string}
*/
function
getImageSrcHTML
(
node
:
HtmlNode
):
string
{
const
srcAttr
=
getAttribute
(
node
,
'
src
'
);
if
(
!
srcAttr
)
{
console
.
warn
(
'
No "src" attribute in
'
,
node
&&
node
.
open
);
return
;
}
return
(
<
HtmlToken
>
srcAttr
.
value
).
value
;
}
/**
* Returns image source from given `url()` token
* @param {Property} node
* @param {Position}
* @return {string}
*/
function
getImageSrcCSS
(
node
:
Property
,
position
:
Position
):
string
{
const
urlToken
=
findUrlToken
(
node
,
position
);
if
(
!
urlToken
)
{
return
;
}
// A stylesheet token may contain either quoted ('string') or unquoted URL
let
urlValue
=
urlToken
.
item
(
0
);
if
(
urlValue
&&
urlValue
.
type
===
'
string
'
)
{
urlValue
=
urlValue
.
item
(
0
);
}
return
urlValue
&&
urlValue
.
valueOf
();
}
/**
* Updates size of given HTML node
* @param {TextEditor} editor
* @param {HtmlNode} node
* @param {number} width
* @param {number} height
*/
function
updateHTMLTag
(
editor
:
TextEditor
,
node
:
HtmlNode
,
width
:
number
,
height
:
number
)
{
const
srcAttr
=
getAttribute
(
node
,
'
src
'
);
const
widthAttr
=
getAttribute
(
node
,
'
width
'
);
const
heightAttr
=
getAttribute
(
node
,
'
height
'
);
let
edits
:
[
Range
,
string
][]
=
[];
// apply changes from right to left, first for height, then for width
let
point
:
Position
;
const
quote
=
getAttributeQuote
(
editor
,
widthAttr
||
heightAttr
||
srcAttr
);
if
(
!
heightAttr
)
{
// no `height` attribute, add it right after `width` or `src`
point
=
widthAttr
?
widthAttr
.
end
:
srcAttr
.
end
;
edits
.
push
([
new
Range
(
point
,
point
),
` height=
${
quote
}${
height
}${
quote
}
`
]);
}
else
{
edits
.
push
([
new
Range
(
heightAttr
.
value
.
start
,
heightAttr
.
value
.
end
),
String
(
height
)]);
}
if
(
!
widthAttr
)
{
// no `width` attribute, add it right before `height` or after `src`
point
=
heightAttr
?
heightAttr
.
start
:
srcAttr
.
end
;
edits
.
push
([
new
Range
(
point
,
point
),
` width=
${
quote
}${
width
}${
quote
}
`
]);
}
else
{
edits
.
push
([
new
Range
(
widthAttr
.
value
.
start
,
widthAttr
.
value
.
end
),
String
(
width
)]);
}
return
editor
.
edit
(
builder
=>
{
edits
.
forEach
(([
rangeToReplace
,
textToReplace
])
=>
{
builder
.
replace
(
rangeToReplace
,
textToReplace
);
});
});
}
/**
* Updates size of given CSS rule
* @param {TextEditor} editor
* @param {Property} srcProp
* @param {number} width
* @param {number} height
*/
function
updateCSSNode
(
editor
:
TextEditor
,
srcProp
:
Property
,
width
:
number
,
height
:
number
)
{
const
rule
=
srcProp
.
parent
;
const
widthProp
=
getProperty
(
rule
,
'
width
'
);
const
heightProp
=
getProperty
(
rule
,
'
height
'
);
// Detect formatting
const
separator
=
srcProp
.
separator
||
'
:
'
;
const
before
=
getBefore
(
editor
,
srcProp
);
let
edits
:
[
Range
,
string
][]
=
[];
if
(
!
srcProp
.
terminatorToken
)
{
edits
.
push
([
new
Range
(
srcProp
.
end
,
srcProp
.
end
),
'
;
'
]);
}
let
point
:
Position
;
if
(
!
heightProp
)
{
// no `height` property, add it right after `width` or source property
point
=
widthProp
?
widthProp
.
start
:
srcProp
.
end
;
edits
.
push
([
new
Range
(
point
,
point
),
`
${
before
}
height
${
separator
}${
height
}
px;`
]);
}
else
{
edits
.
push
([
new
Range
(
heightProp
.
valueToken
.
start
,
heightProp
.
valueTokenend
),
`
${
height
}
px`
]);
}
if
(
!
widthProp
)
{
// no `width` attribute, add it right after `height` or source property
if
(
heightProp
)
{
point
=
heightProp
.
previousSibling
?
heightProp
.
previousSibling
.
end
:
rule
.
contentStartToken
.
end
;
}
else
{
point
=
srcProp
.
end
;
}
edits
.
push
([
new
Range
(
point
,
point
),
`
${
before
}
width
${
separator
}${
width
}
px;`
]);
}
else
{
edits
.
push
([
new
Range
(
widthProp
.
valueToken
.
start
,
widthProp
.
valueTokenend
),
`
${
width
}
px`
]);
}
return
editor
.
edit
(
builder
=>
{
edits
.
forEach
(([
rangeToReplace
,
textToReplace
])
=>
{
builder
.
replace
(
rangeToReplace
,
textToReplace
);
});
});
}
/**
* Returns attribute object with `attrName` name from given HTML node
* @param {Node} node
* @param {String} attrName
* @return {Object}
*/
function
getAttribute
(
node
,
attrName
):
Attribute
{
attrName
=
attrName
.
toLowerCase
();
return
node
&&
node
.
open
.
attributes
.
find
(
attr
=>
attr
.
name
.
value
.
toLowerCase
()
===
attrName
);
}
/**
* Returns quote character, used for value of given attribute. May return empty
* string if attribute wasn’t quoted
* @param {TextEditor} editor
* @param {Object} attr
* @return {String}
*/
function
getAttributeQuote
(
editor
,
attr
)
{
const
range
=
new
Range
(
attr
.
value
?
attr
.
value
.
end
:
attr
.
end
,
attr
.
end
);
return
range
.
isEmpty
?
''
:
editor
.
document
.
getText
(
range
);
}
/**
* Finds 'url' token for given `pos` point in given CSS property `node`
* @param {Node} node
* @param {Position} pos
* @return {Token}
*/
function
findUrlToken
(
node
,
pos
:
Position
)
{
for
(
let
i
=
0
,
il
=
node
.
parsedValue
.
length
,
url
;
i
<
il
;
i
++
)
{
iterateCSSToken
(
node
.
parsedValue
[
i
],
(
token
:
CssToken
)
=>
{
if
(
token
.
type
===
'
url
'
&&
token
.
start
.
isBeforeOrEqual
(
pos
)
&&
token
.
end
.
isAfterOrEqual
(
pos
))
{
url
=
token
;
return
false
;
}
});
if
(
url
)
{
return
url
;
}
}
}
/**
* Returns `name` CSS property from given `rule`
* @param {Node} rule
* @param {String} name
* @return {Node}
*/
function
getProperty
(
rule
,
name
)
{
return
rule
.
children
.
find
(
node
=>
node
.
type
===
'
property
'
&&
node
.
name
===
name
);
}
/**
* Returns a string that is used to delimit properties in current node’s rule
* @param {TextEditor} editor
* @param {Node} node
* @return {String}
*/
function
getBefore
(
editor
:
TextEditor
,
node
:
Property
)
{
let
anchor
;
if
(
anchor
=
(
node
.
previousSibling
||
node
.
parent
.
contentStartToken
))
{
return
editor
.
document
.
getText
(
new
Range
(
anchor
.
end
,
node
.
start
));
}
else
if
(
anchor
=
(
node
.
nextSibling
||
node
.
parent
.
contentEndToken
))
{
return
editor
.
document
.
getText
(
new
Range
(
node
.
end
,
anchor
.
start
));
}
return
''
;
}
extensions/emmet/src/util.ts
浏览文件 @
0bee43b5
...
...
@@ -6,7 +6,7 @@
import
*
as
vscode
from
'
vscode
'
;
import
parse
from
'
@emmetio/html-matcher
'
;
import
parseStylesheet
from
'
@emmetio/css-parser
'
;
import
{
Node
,
HtmlNode
}
from
'
EmmetNode
'
;
import
{
Node
,
HtmlNode
,
CssToken
}
from
'
EmmetNode
'
;
import
{
DocumentStreamReader
}
from
'
./bufferStream
'
;
import
{
isStyleSheet
}
from
'
vscode-emmet-helper
'
;
...
...
@@ -262,3 +262,17 @@ export function getEmmetConfiguration() {
variables
:
emmetConfig
[
'
variables
'
]
};
}
/**
* Itereates by each child, as well as nested child’ children, in their order
* and invokes `fn` for each. If `fn` function returns `false`, iteration stops
* @param {Token} token
* @param {Function} fn
*/
export
function
iterateCSSToken
(
token
:
CssToken
,
fn
)
{
for
(
let
i
=
0
,
il
=
token
.
size
;
i
<
il
;
i
++
)
{
if
(
fn
(
token
.
item
(
i
))
===
false
||
iterateCSSToken
(
token
.
item
(
i
),
fn
)
===
false
)
{
return
false
;
}
}
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录