Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
李少辉-开发者
gitlab-foss
提交
8dcd03f2
G
gitlab-foss
项目概览
李少辉-开发者
/
gitlab-foss
通知
15
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
G
gitlab-foss
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
8dcd03f2
编写于
1月 04, 2016
作者:
J
Jacob Schatz
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
initial new menu
上级
d33cc4e5
变更
8
展开全部
隐藏空白更改
内联
并排
Showing
8 changed file
with
1344 addition
and
903 deletion
+1344
-903
app/assets/javascripts/issues.js.coffee
app/assets/javascripts/issues.js.coffee
+6
-6
app/assets/javascripts/jquery.multiawesome.js
app/assets/javascripts/jquery.multiawesome.js
+354
-0
app/assets/javascripts/multiawesome.js
app/assets/javascripts/multiawesome.js
+390
-0
app/assets/stylesheets/application.scss
app/assets/stylesheets/application.scss
+5
-0
app/assets/stylesheets/multiawesome.css
app/assets/stylesheets/multiawesome.css
+105
-0
app/views/shared/issuable/_filter.html.haml
app/views/shared/issuable/_filter.html.haml
+13
-0
db/schema.rb
db/schema.rb
+0
-897
vendor/assets/javascripts/sifter.js
vendor/assets/javascripts/sifter.js
+471
-0
未找到文件。
app/assets/javascripts/issues.js.coffee
浏览文件 @
8dcd03f2
...
...
@@ -16,12 +16,12 @@
else
$
(
this
).
html
totalIssues
-
1
$
(
"body"
).
on
"click"
,
".issues-other-filters .dropdown-menu a"
,
->
$
(
'.issues-list'
).
block
(
message
:
null
,
overlayCSS
:
backgroundColor
:
'#DDD'
opacity
:
.
4
)
#
$('.issues-list').block(
#
message: null,
#
overlayCSS:
#
backgroundColor: '#DDD'
#
opacity: .4
#
)
reload
:
->
Issues
.
initSelects
()
...
...
app/assets/javascripts/jquery.multiawesome.js
0 → 100644
浏览文件 @
8dcd03f2
//= require sifter.js
/**
* multiawesome.js (v0.12.1)
* Copyright (c) 2015 Jacob Schatz & contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
* file except in compliance with the License. You may obtain a copy of the License at:
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
* ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*
* @author Jacob Schatz <jschatz@gitlab.com>
*/
/*jshint curly:false */
/*jshint browser:true */
(
function
(
$
)
{
$
.
fn
.
multiawesome
=
function
(
settings
)
{
settings
=
settings
||
{};
var
MultiAwesome
=
{
searchTemplate
:
'
<li><div class="input-with-icon"><i class="fa fa-search"></i><input type="text" id="multiawesome-search-input" /></div></li>
'
,
itemContainerTemplate
:
'
<li class="dropdown-multi-menu-selections"><ul></ul></li>
'
,
itemTemplate
:
'
<li><a href="#" class="item" tabIndex="-1"><input type="checkbox" name="{{name}}" value="{{data}}"/>{{label}}</a></li>
'
,
seperatorTemplate
:
'
<li role="separator" class="divider"></li>
'
,
titleTemplate
:
'
<li class=dropdown-multi-menu-title><div class=dropdown-multi-menu-title-area><h3>{{title}}</h3></div></li>
'
,
categoryContainerTemplate
:
'
<li class=dropdown-multi-menu-category><ul></ul></li>
'
,
categoryItemTemplate
:
'
<li><a href="#" class="category" tabIndex="-1"><input type="checkbox" value="{{category}}"/>{{category}}</a></li>
'
,
tipTemplate
:
'
<li class=dropdown-multi-menu-tip><div class=dropdown-multi-menu-tip-area><p>{{tip}}</p></div></li>
'
,
minSearchLength
:
2
,
};
return
this
.
each
(
function
()
{
var
self
=
this
,
categories
=
[],
$self
=
$
(
self
),
$form
=
$
(
self
).
closest
(
'
form
'
),
$itemContainer
,
$itemContainerUL
,
itemTemplate
,
itemContainerTemplate
,
categoriesSet
=
false
,
selectedItems
=
[],
selectedCategories
=
[],
$searchInput
,
multiple
=
typeof
$self
.
attr
(
'
data-multiple
'
)
!==
'
undefined
'
,
currentData
=
settings
.
data
||
$self
.
data
(
'
data
'
),
inputName
=
$
(
self
).
data
(
'
name
'
),
sifter
,
dataObject
=
{
label
:
'
label
'
,
data
:
'
data
'
,
category
:
'
category
'
};
if
(
self
.
tagName
!==
'
UL
'
)
{
return
;
}
var
prepareDropdown
=
function
()
{
var
placeholder
=
$self
.
data
(
'
placeholder
'
);
if
(
typeof
settings
.
searchTemplate
!==
'
undefined
'
)
{
MultiAwesome
.
searchTemplate
=
settings
.
searchTemplate
;
}
$self
.
prepend
(
MultiAwesome
.
searchTemplate
);
if
(
placeholder
)
{
$
(
'
#multiawesome-search-input
'
).
prop
(
'
placeholder
'
,
placeholder
);
}
};
var
attachBtnListeners
=
function
()
{
$
(
'
ul.dropdown-multi-menu
'
).
on
(
'
click
'
,
'
li.dropdown-multi-menu-category a
'
,
dropdownCategoryLinkClicked
);
$
(
'
ul.dropdown-multi-menu
'
).
on
(
'
click
'
,
'
li.dropdown-multi-menu-selections a
'
,
dropdownSelectionLinkClicked
);
$searchInput
=
$
(
'
#multiawesome-search-input
'
);
$searchInput
.
on
(
'
keydown keyup update
'
,
inputSearched
);
};
var
parseSearchResults
=
function
(
results
)
{
var
finalData
=
[];
results
.
forEach
(
function
(
result
)
{
finalData
.
push
(
currentData
[
result
.
id
]);
});
renderData
(
finalData
);
};
var
inputSearched
=
function
()
{
var
minSearchLength
=
settings
.
minSearchLength
||
MultiAwesome
.
minSearchLength
;
//remove current hidden inputs
$
(
'
input[type="hidden"][name="
'
+
inputName
+
'
"]
'
).
remove
();
if
(
$searchInput
.
val
().
length
>
minSearchLength
)
{
var
results
=
sifter
.
search
(
$searchInput
.
val
(),
{
fields
:
[
dataObject
.
label
],
sort
:
[{
field
:
dataObject
.
label
,
direction
:
'
asc
'
}]
});
parseSearchResults
(
results
.
items
);
}
else
{
renderData
(
currentData
);
}
};
var
configureData
=
function
()
{
if
(
typeof
settings
.
itemLabelTitle
!==
'
undefined
'
)
{
dataObject
.
label
=
settings
.
itemLabelTitle
;
}
if
(
typeof
settings
.
itemDataTitle
!==
'
undefined
'
)
{
dataObject
.
data
=
settings
.
itemDataTitle
;
}
if
(
typeof
settings
.
itemCategoryTitle
!==
'
undefined
'
)
{
dataObject
.
category
=
settings
.
itemCategoryTitle
;
}
};
var
addCategories
=
function
()
{
var
$categoryContainer
;
var
$categoryContainerUL
;
if
(
categories
.
length
)
{
if
(
typeof
settings
.
categoryContainerTemplate
!==
'
undefined
'
)
{
$categoryContainer
=
$
(
settings
.
categoryContainerTemplate
);
}
else
{
$categoryContainer
=
$
(
MultiAwesome
.
categoryContainerTemplate
);
}
$categoryContainerUL
=
$categoryContainer
.
find
(
'
ul
'
);
$self
.
prepend
(
MultiAwesome
.
seperatorTemplate
);
categories
.
forEach
(
function
(
category
)
{
$categoryContainerUL
.
prepend
(
MultiAwesome
.
categoryItemTemplate
.
replace
(
/{{category}}/g
,
category
)
);
});
$self
.
prepend
(
$categoryContainer
);
}
};
var
addTitle
=
function
()
{
var
titleTemplate
;
var
titleData
=
$self
.
data
(
'
title
'
)
||
settings
.
title
;
if
(
typeof
titleData
!==
'
undefined
'
)
{
if
(
typeof
settings
.
titleTemplate
!==
'
undefined
'
)
{
titleTemplate
=
settings
.
titleTemplate
;
}
else
{
titleTemplate
=
MultiAwesome
.
titleTemplate
;
}
$self
.
prepend
(
MultiAwesome
.
seperatorTemplate
);
$self
.
prepend
(
titleTemplate
.
replace
(
/{{title}}/g
,
titleData
)
);
}
};
var
addData
=
function
(
callback
)
{
function
parseDataWhenReady
()
{
itemTemplate
=
MultiAwesome
.
itemTemplate
;
itemContainerTemplate
=
MultiAwesome
.
itemContainerTemplate
;
if
(
typeof
settings
.
itemTemplate
!==
'
undefined
'
)
{
itemTemplate
=
settings
.
itemTemplate
;
}
if
(
typeof
settings
.
itemContainerTemplate
!==
'
undefined
'
)
{
itemContainerTemplate
=
settings
.
itemContainerTemplate
;
}
$itemContainer
=
$
(
itemContainerTemplate
);
$itemContainerUL
=
$itemContainer
.
find
(
'
ul
'
);
sifter
=
new
Sifter
(
currentData
);
renderData
(
currentData
,
callback
);
}
configureData
();
if
(
typeof
currentData
!==
'
undefined
'
)
{
if
(
typeof
currentData
===
'
string
'
)
{
$
.
getJSON
(
currentData
,
function
(
data
)
{
currentData
=
data
;
parseDataWhenReady
();
});
}
else
if
(
typeof
currentData
===
'
object
'
)
{
parseDataWhenReady
();
}
}
};
var
renderData
=
function
(
data
,
callback
)
{
selectedItems
=
[];
$itemContainerUL
.
empty
();
var
emptyObj
=
{};
var
skipMatch
=
false
;
if
(
!
data
.
length
)
{
emptyObj
[
dataObject
.
label
]
=
'
No matches found
'
;
emptyObj
[
dataObject
.
data
]
=
''
;
emptyObj
[
dataObject
.
category
]
=
''
;
data
.
push
(
emptyObj
);
skipMatch
=
true
;
}
data
.
forEach
(
function
(
item
)
{
if
(
!
categoriesSet
&&
!
skipMatch
)
{
var
addCategory
=
item
[
dataObject
.
category
];
if
(
item
.
hasOwnProperty
(
dataObject
.
category
)
&&
categories
.
indexOf
(
addCategory
)
===
-
1
)
{
categories
.
push
(
addCategory
);
}
}
else
{
// only do this if the categories are already set... they won't search categories on the first time.
if
(
selectedCategories
.
length
&&
selectedCategories
.
indexOf
(
item
[
dataObject
.
category
])
===
-
1
&&
!
skipMatch
)
{
return
;
}
}
$itemContainerUL
.
append
(
itemTemplate
.
replace
(
/{{data}}/g
,
item
[
dataObject
.
data
])
.
replace
(
/{{label}}/g
,
item
[
dataObject
.
label
])
.
replace
(
/{{name}}/g
,
'
_
'
+
inputName
)
);
});
if
(
!
categoriesSet
)
{
$self
.
append
(
$itemContainer
);
}
if
(
categories
.
length
){
categoriesSet
=
true
;
}
if
(
callback
)
{
callback
();
}
};
var
addToForm
=
function
(
val
)
{
$form
.
prepend
(
'
<input type="hidden" name="
'
+
inputName
+
'
" value="
'
+
val
+
'
" />
'
);
};
var
removeFromForm
=
function
(
val
)
{
$form
.
find
(
'
input[name="
'
+
inputName
+
'
"][value="
'
+
val
+
'
"]
'
)
.
remove
();
};
/* * * * * * * * * * * * * * * */
/* listeners
/* * * * * * * * * * * * * * * */
var
dropdownInputClicked
=
function
(
e
)
{
return
false
;
};
var
dropdownCategoryLinkClicked
=
function
(
e
)
{
var
$target
=
$
(
e
.
currentTarget
),
$inp
=
$target
.
find
(
'
input
'
),
val
=
$inp
.
val
(),
i
=
selectedCategories
.
indexOf
(
val
);
if
(
i
>
-
1
)
{
var
spliced
=
selectedCategories
.
splice
(
i
,
1
);
$target
.
removeClass
(
'
selected
'
);
setTimeout
(
function
()
{
$inp
.
prop
(
'
checked
'
,
false
);
},
0
);
}
else
{
selectedCategories
.
push
(
val
);
$target
.
addClass
(
'
selected
'
);
setTimeout
(
function
()
{
$inp
.
prop
(
'
checked
'
,
true
);
},
0
);
}
inputSearched
();
$
(
e
.
target
).
blur
();
return
false
;
};
var
dropdownSelectionLinkClicked
=
function
(
e
)
{
var
$target
=
$
(
e
.
currentTarget
),
$inp
=
$target
.
find
(
'
input
'
),
val
=
$inp
.
val
(),
i
=
selectedItems
.
indexOf
(
val
);
if
(
i
>
-
1
)
{
var
spliced
=
selectedItems
.
splice
(
i
,
1
);
if
(
multiple
)
{
removeFromForm
(
spliced
);
}
else
{
$form
.
find
(
'
input[name="
'
+
inputName
+
'
"]
'
)
.
remove
();
}
$target
.
removeClass
(
'
selected
'
);
setTimeout
(
function
()
{
$inp
.
prop
(
'
checked
'
,
false
);
},
0
);
}
else
{
if
(
!
multiple
)
{
selectedItems
=
[];
$form
.
find
(
'
input[name="
'
+
inputName
+
'
"]
'
)
.
remove
();
$form
.
find
(
'
input[name="_
'
+
inputName
+
'
"]
'
)
.
parent
()
.
removeClass
(
'
selected
'
);
}
selectedItems
.
push
(
val
);
addToForm
(
val
);
$target
.
addClass
(
'
selected
'
);
setTimeout
(
function
()
{
$inp
.
prop
(
'
checked
'
,
true
);
},
0
);
}
$
(
e
.
target
).
blur
();
return
false
;
};
var
addTip
=
function
()
{
var
tipTemplate
;
var
tipData
=
$self
.
data
(
'
tip
'
)
||
settings
.
tip
;
if
(
typeof
tipData
!==
'
undefined
'
)
{
if
(
typeof
settings
.
tipTemplate
!==
'
undefined
'
)
{
tipTemplate
=
settings
.
tipTemplate
;
}
else
{
tipTemplate
=
MultiAwesome
.
tipTemplate
;
}
$self
.
append
(
MultiAwesome
.
seperatorTemplate
);
$self
.
append
(
tipTemplate
.
replace
(
/{{tip}}/g
,
tipData
)
);
}
};
/* * * * * * * * * * * * * * * */
/* setup
/* * * * * * * * * * * * * * * */
var
setup
=
function
()
{
addData
(
function
(){
addCategories
();
prepareDropdown
();
addTitle
();
attachBtnListeners
();
addTip
();
});
};
setup
();
});
};
})(
jQuery
);
\ No newline at end of file
app/assets/javascripts/multiawesome.js
0 → 100644
浏览文件 @
8dcd03f2
//= require sifter
/**
* multiawesome.js (v0.12.1)
* Copyright (c) 2015 Jacob Schatz & contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
* file except in compliance with the License. You may obtain a copy of the License at:
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
* ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*
* @author Jacob Schatz <jschatz@gitlab.com>
*/
/*jshint curly:false */
/*jshint browser:true */
(
function
(
$
)
{
'
use strict
'
;
$
.
fn
.
multiawesome
=
function
(
settings
)
{
settings
=
settings
||
{};
var
MultiAwesome
=
{
searchTemplate
:
'
<li><div class="input-with-icon"><i class="fa fa-search"></i><input type="text" id="multiawesome-search-input" /></div></li>
'
,
itemContainerTemplate
:
'
<li class="dropdown-multi-menu-selections"><ul></ul></li>
'
,
itemTemplate
:
'
<li><a href="#" class="item" tabIndex="-1"><input type="checkbox" name="{{name}}" value="{{data}}"/>{{label}}</a></li>
'
,
seperatorTemplate
:
'
<li role="separator" class="divider"></li>
'
,
titleTemplate
:
'
<li class=dropdown-multi-menu-title><div class=dropdown-multi-menu-title-area><h3>{{title}}</h3></div></li>
'
,
categoryContainerTemplate
:
'
<li class=dropdown-multi-menu-category><ul></ul></li>
'
,
categoryItemTemplate
:
'
<li><a href="#" class="category" tabIndex="-1"><input type="checkbox" value="{{category}}"/>{{category}}</a></li>
'
,
tipTemplate
:
'
<li class=dropdown-multi-menu-tip><div class=dropdown-multi-menu-tip-area><p>{{tip}}</p></div></li>
'
,
minSearchLength
:
2
};
return
this
.
each
(
function
()
{
var
self
=
this
,
categories
=
[],
$self
=
$
(
self
),
$form
=
$
(
self
).
closest
(
'
form
'
),
$itemContainer
,
$itemContainerUL
,
itemTemplate
,
itemContainerTemplate
,
categoriesSet
=
false
,
selectedItems
=
[],
selectedCategories
=
[],
$searchInput
,
onChange
=
settings
.
onChange
||
function
(){},
multiple
=
typeof
$self
.
attr
(
'
data-multiple
'
)
!==
'
undefined
'
,
currentData
=
settings
.
data
||
$self
.
data
(
'
data
'
),
inputName
=
$
(
self
).
data
(
'
name
'
),
sifter
,
alwaysData
=
settings
.
always
||
[],
dataObject
=
{
label
:
'
label
'
,
data
:
'
data
'
,
category
:
'
category
'
};
if
(
self
.
tagName
!==
'
UL
'
)
{
return
;
}
var
prepareDropdown
=
function
()
{
var
placeholder
=
$self
.
data
(
'
placeholder
'
);
if
(
typeof
settings
.
searchTemplate
!==
'
undefined
'
)
{
MultiAwesome
.
searchTemplate
=
settings
.
searchTemplate
;
}
$self
.
prepend
(
MultiAwesome
.
searchTemplate
);
if
(
placeholder
)
{
$
(
'
#multiawesome-search-input
'
).
prop
(
'
placeholder
'
,
placeholder
);
}
};
var
attachBtnListeners
=
function
()
{
$
(
'
ul.dropdown-multi-menu
'
).
on
(
'
click
'
,
'
li.dropdown-multi-menu-category a
'
,
dropdownCategoryLinkClicked
);
$
(
'
ul.dropdown-multi-menu
'
).
on
(
'
click
'
,
'
li.dropdown-multi-menu-selections a
'
,
dropdownSelectionLinkClicked
);
$searchInput
=
$
(
'
#multiawesome-search-input
'
);
$searchInput
.
on
(
'
keydown keyup update
'
,
inputSearched
);
};
var
parseSearchResults
=
function
(
results
)
{
var
finalData
=
[];
results
.
forEach
(
function
(
result
)
{
finalData
.
push
(
currentData
[
result
.
id
]);
});
renderData
(
finalData
);
};
var
inputSearched
=
function
()
{
var
minSearchLength
=
settings
.
minSearchLength
||
MultiAwesome
.
minSearchLength
;
//remove current hidden inputs
$
(
'
input[type="hidden"][name="
'
+
inputName
+
'
"]
'
).
remove
();
if
(
$searchInput
.
val
().
length
>
minSearchLength
)
{
var
results
=
sifter
.
search
(
$searchInput
.
val
(),
{
fields
:
[
dataObject
.
label
],
sort
:
[{
field
:
dataObject
.
label
,
direction
:
'
asc
'
}]
});
parseSearchResults
(
results
.
items
);
}
else
{
renderData
(
currentData
);
}
};
var
configureData
=
function
()
{
if
(
typeof
settings
.
itemLabelTitle
!==
'
undefined
'
)
{
dataObject
.
label
=
settings
.
itemLabelTitle
;
}
if
(
typeof
settings
.
itemDataTitle
!==
'
undefined
'
)
{
dataObject
.
data
=
settings
.
itemDataTitle
;
}
if
(
typeof
settings
.
itemCategoryTitle
!==
'
undefined
'
)
{
dataObject
.
category
=
settings
.
itemCategoryTitle
;
}
};
var
addCategories
=
function
()
{
var
$categoryContainer
;
var
$categoryContainerUL
;
if
(
categories
.
length
)
{
if
(
typeof
settings
.
categoryContainerTemplate
!==
'
undefined
'
)
{
$categoryContainer
=
$
(
settings
.
categoryContainerTemplate
);
}
else
{
$categoryContainer
=
$
(
MultiAwesome
.
categoryContainerTemplate
);
}
$categoryContainerUL
=
$categoryContainer
.
find
(
'
ul
'
);
$self
.
prepend
(
MultiAwesome
.
seperatorTemplate
);
categories
.
forEach
(
function
(
category
)
{
$categoryContainerUL
.
prepend
(
MultiAwesome
.
categoryItemTemplate
.
replace
(
/
\{\{
category
\}\}
/g
,
category
)
);
});
$self
.
prepend
(
$categoryContainer
);
}
};
var
addTitle
=
function
()
{
var
titleTemplate
;
var
titleData
=
$self
.
data
(
'
title
'
)
||
settings
.
title
;
if
(
typeof
titleData
!==
'
undefined
'
)
{
if
(
typeof
settings
.
titleTemplate
!==
'
undefined
'
)
{
titleTemplate
=
settings
.
titleTemplate
;
}
else
{
titleTemplate
=
MultiAwesome
.
titleTemplate
;
}
$self
.
prepend
(
MultiAwesome
.
seperatorTemplate
);
$self
.
prepend
(
titleTemplate
.
replace
(
/
\{\{
title
\}\}
/g
,
titleData
)
);
}
};
var
addData
=
function
(
callback
)
{
function
parseDataWhenReady
()
{
itemTemplate
=
MultiAwesome
.
itemTemplate
;
itemContainerTemplate
=
MultiAwesome
.
itemContainerTemplate
;
if
(
typeof
settings
.
itemTemplate
!==
'
undefined
'
)
{
itemTemplate
=
settings
.
itemTemplate
;
}
if
(
typeof
settings
.
itemContainerTemplate
!==
'
undefined
'
)
{
itemContainerTemplate
=
settings
.
itemContainerTemplate
;
}
$itemContainer
=
$
(
itemContainerTemplate
);
$itemContainerUL
=
$itemContainer
.
find
(
'
ul
'
);
sifter
=
new
Sifter
(
currentData
);
renderData
(
currentData
,
callback
);
}
configureData
();
if
(
typeof
currentData
!==
'
undefined
'
)
{
if
(
typeof
currentData
===
'
string
'
)
{
$
.
getJSON
(
currentData
,
function
(
data
)
{
currentData
=
data
;
parseDataWhenReady
();
});
}
else
if
(
typeof
currentData
===
'
object
'
)
{
parseDataWhenReady
();
}
}
};
var
renderData
=
function
(
data
,
callback
)
{
selectedItems
=
[];
$itemContainerUL
.
empty
();
var
emptyObj
=
{};
var
skipMatch
=
false
;
if
(
!
data
.
length
)
{
emptyObj
[
dataObject
.
label
]
=
'
No matches found
'
;
emptyObj
[
dataObject
.
data
]
=
''
;
emptyObj
[
dataObject
.
category
]
=
''
;
data
.
push
(
emptyObj
);
skipMatch
=
true
;
}
data
=
data
.
concat
(
alwaysData
);
data
.
forEach
(
function
(
item
)
{
if
(
!
categoriesSet
&&
!
skipMatch
)
{
var
addCategory
=
item
[
dataObject
.
category
];
if
(
item
.
hasOwnProperty
(
dataObject
.
category
)
&&
categories
.
indexOf
(
addCategory
)
===
-
1
)
{
categories
.
push
(
addCategory
);
}
}
else
{
// only do this if the categories are already set... they won't search categories on the first time.
if
(
selectedCategories
.
length
&&
selectedCategories
.
indexOf
(
item
[
dataObject
.
category
])
===
-
1
&&
!
skipMatch
)
{
return
;
}
}
$itemContainerUL
.
append
(
itemTemplate
.
replace
(
/
\{\{
data
\}\}
/g
,
item
[
dataObject
.
data
])
.
replace
(
/
\{\{
label
\}\}
/g
,
item
[
dataObject
.
label
])
.
replace
(
/
\{\{
name
\}\}
/g
,
'
_
'
+
inputName
)
);
});
if
(
!
categoriesSet
)
{
$self
.
append
(
$itemContainer
);
}
if
(
categories
.
length
){
categoriesSet
=
true
;
}
if
(
callback
)
{
callback
();
}
};
var
addToForm
=
function
(
val
)
{
$form
.
prepend
(
'
<input type="hidden" name="
'
+
inputName
+
'
" value="
'
+
val
+
'
" />
'
);
};
var
removeFromForm
=
function
(
val
)
{
$form
.
find
(
'
input[name="
'
+
inputName
+
'
"][value="
'
+
val
+
'
"]
'
)
.
remove
();
};
/* * * * * * * * * * * * * * * */
/* listeners
/* * * * * * * * * * * * * * * */
var
dropdownInputClicked
=
function
(
e
)
{
return
false
;
};
var
dropdownCategoryLinkClicked
=
function
(
e
)
{
var
$target
=
$
(
e
.
currentTarget
),
$inp
=
$target
.
find
(
'
input
'
),
val
=
$inp
.
val
(),
i
=
selectedCategories
.
indexOf
(
val
);
e
.
preventDefault
();
if
(
i
>
-
1
)
{
var
spliced
=
selectedCategories
.
splice
(
i
,
1
);
$target
.
removeClass
(
'
selected
'
);
setTimeout
(
function
()
{
$inp
.
prop
(
'
checked
'
,
false
);
},
0
);
}
else
{
selectedCategories
.
push
(
val
);
$target
.
addClass
(
'
selected
'
);
setTimeout
(
function
()
{
$inp
.
prop
(
'
checked
'
,
true
);
},
0
);
}
inputSearched
();
$
(
e
.
target
).
blur
();
return
false
;
};
var
findItemWithData
=
function
(
searchData
,
id
)
{
var
item
=
{};
for
(
var
i
=
searchData
.
length
-
1
;
i
>=
0
;
i
--
)
{
item
=
searchData
[
i
];
if
(
item
.
hasOwnProperty
(
dataObject
.
data
)
&&
item
[
dataObject
.
data
]
==
id
)
{
return
item
;
}
};
return
undefined
;
};
var
dropdownSelectionLinkClicked
=
function
(
e
)
{
var
$target
=
$
(
e
.
currentTarget
),
$inp
=
$target
.
find
(
'
input
'
),
findItemInData
,
val
=
$inp
.
val
(),
i
=
selectedItems
.
indexOf
(
val
);
e
.
preventDefault
();
if
(
i
>
-
1
)
{
var
spliced
=
selectedItems
.
splice
(
i
,
1
);
if
(
multiple
)
{
removeFromForm
(
spliced
);
}
else
{
$form
.
find
(
'
input[name="
'
+
inputName
+
'
"]
'
)
.
remove
();
}
$target
.
removeClass
(
'
selected
'
);
setTimeout
(
function
()
{
$inp
.
prop
(
'
checked
'
,
false
);
},
0
);
}
else
{
if
(
!
multiple
)
{
selectedItems
=
[];
$form
.
find
(
'
input[name="
'
+
inputName
+
'
"]
'
)
.
remove
();
$form
.
find
(
'
input[name="_
'
+
inputName
+
'
"]
'
)
.
parent
()
.
removeClass
(
'
selected
'
);
}
findItemInData
=
findItemWithData
(
currentData
,
val
);
if
(
typeof
findItemInData
===
'
undefined
'
)
{
findItemInData
=
findItemWithData
(
alwaysData
,
val
)
}
onChange
(
findItemInData
);
selectedItems
.
push
(
val
);
addToForm
(
val
);
$target
.
addClass
(
'
selected
'
);
setTimeout
(
function
()
{
$inp
.
prop
(
'
checked
'
,
true
);
},
0
);
}
if
(
multiple
)
{
// close the dropdown if single selection
$
(
e
.
target
).
blur
();
return
false
;
}
else
{
var
button
=
$self
.
prevAll
(
'
.dropdown-toggle
'
);
$self
.
prevAll
(
'
.dropdown-toggle
'
)
.
contents
()
.
each
(
function
(){
if
(
this
.
nodeType
===
3
&&
this
.
nodeValue
.
trim
()
)
{
this
.
textContent
=
$target
.
text
();
}
});
}
};
var
addTip
=
function
()
{
var
tipTemplate
;
var
tipData
=
$self
.
data
(
'
tip
'
)
||
settings
.
tip
;
if
(
typeof
tipData
!==
'
undefined
'
)
{
if
(
typeof
settings
.
tipTemplate
!==
'
undefined
'
)
{
tipTemplate
=
settings
.
tipTemplate
;
}
else
{
tipTemplate
=
MultiAwesome
.
tipTemplate
;
}
$self
.
append
(
MultiAwesome
.
seperatorTemplate
);
$self
.
append
(
tipTemplate
.
replace
(
/
\{\{
tip
\}\}
/g
,
tipData
)
);
}
};
/* * * * * * * * * * * * * * * */
/* setup
/* * * * * * * * * * * * * * * */
var
setup
=
function
()
{
addData
(
function
(){
addCategories
();
prepareDropdown
();
addTitle
();
attachBtnListeners
();
addTip
();
});
};
setup
();
});
};
})(
jQuery
);
\ No newline at end of file
app/assets/stylesheets/application.scss
浏览文件 @
8dcd03f2
...
...
@@ -49,3 +49,8 @@
* Styles for JS behaviors.
*/
@import
"behaviors.scss"
;
/*
* Styles for multiawesome
*/
@import
"multiawesome"
;
\ No newline at end of file
app/assets/stylesheets/multiawesome.css
0 → 100644
浏览文件 @
8dcd03f2
.open
>
.dropdown-multi-menu
{
max-width
:
320px
;
border-radius
:
0px
;
}
.dropdown-multi-menu
input
[
type
=
'text'
]
{
margin
:
0
5px
;
width
:
309px
;
height
:
35px
;
padding-left
:
6px
;
}
.dropdown-multi-menu
input
[
type
=
'checkbox'
]
{
margin
:
0
5px
;
display
:
none
;
}
.dropdown-multi-menu
>
li
>
a
{
padding-left
:
0
;
}
.dropdown-multi-menu
.dropdown-multi-menu-title-area
h3
{
font-size
:
15px
;
text-align
:
center
;
margin-top
:
5px
;
}
.dropdown-multi-menu
.dropdown-multi-menu-title-area
h3
::after
{
content
:
""
;
background
:
url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAACGUlEQVQ4jZ2VMXbiMBCGf83KKcxzntOmI1VMEyj2BLpD9gRbwQVygeUCpMsdcgKfQaYJqeIu1RYGC/stNjNbJOaBY8hu/lrz69PoH0mJCBaLhVEArqMoxhf0vFgYARBFUawWT08my7KxiMD3/fhmOLz/H7PE2ruyLL8rpRCG4b0WEQjzeVXXxq3X59ba8zAMZ/1+Pz9l9PLyEiyXy0lRFGZb18bTOhYR6GgwiOdJcu3Wa9R1bZxz5r1mespwuVxOnHO/mBla69jv9R6jwSAmALgZDu9934+JCMyMoiiMtfYuTdOgbZSmaWCtvSuKwjAziOigVbpZGIbhDACKojCnSLMsOyTz/bipPTB879nUWgvnnNkjxdnZ2TMAbDab6zbZaDQ62FCjpS5SIgIAMDOOkTVSItLZ9CRJxnmez5gZzRqlFIgIQRBMhkfiRZ1uADzPeyUiiMiOTERARPA87/VY3VHDr+pDDxtVVXXJzLtjAm9HZmZUVXV5rO5DD9M0DbIsmzSXQkQ4dSntifpA2JWzdmxO5XRnuE92KmddOd0n3Rl+NgGNPpsoAoB5koy7yLpenH6/n49Go2l79udJMgaAbz9ub02+Wv2s69poreNer/cQhuHs4uJi0zbbV1mWcxHJt9sttnVtmPmPc+63VkpBEa205z36vh8fm4C2rq6ucgDTxFqUZbkiolwplavmCwDenvB/MWtr/wv5CwCanfXE6iK0AAAAAElFTkSuQmCC')
;
background-repeat
:
no-repeat
;
background-size
:
11px
;
width
:
12px
;
height
:
12px
;
display
:
inline-block
;
position
:
absolute
;
right
:
13px
;
top
:
13px
;
cursor
:
pointer
;
}
.dropdown-multi-menu
.dropdown-multi-menu-selections
{
max-height
:
150px
;
overflow-y
:
scroll
;
margin-top
:
15px
;
}
.dropdown-multi-menu
.dropdown-multi-menu-category
ul
{
padding-left
:
15px
;
list-style
:
none
;
max-height
:
65px
;
overflow-y
:
scroll
;
}
.dropdown-multi-menu
.dropdown-multi-menu-selections
ul
{
padding-left
:
15px
;
list-style
:
none
;
}
.dropdown-multi-menu
.dropdown-multi-menu-selections
li
{
margin-bottom
:
10px
;
}
.dropdown-multi-menu
.dropdown-multi-menu-selections
ul
a
,
.dropdown-multi-menu
.dropdown-multi-menu-category
ul
a
{
text-decoration
:
none
;
color
:
#333
;
display
:
inline-block
;
width
:
285px
;
text-overflow
:
ellipsis
;
white-space
:
nowrap
;
overflow
:
hidden
;
height
:
30px
;
padding-left
:
30px
;
}
.dropdown-multi-menu
.dropdown-multi-menu-selections
ul
a
.selected
,
.dropdown-multi-menu
.dropdown-multi-menu-category
ul
a
.selected
{
background
:
url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAYCAMAAADat72NAAABtlBMVEX////b29vc29vc3NzZ2dna2dnb2trY19fZ2NjV1dXW1tbT09PU09PV1NTR0dHS0dHS0tLPz8/Qz8/Q0NDNzc3Ozc3Ly8vMy8vMzMzJycnKycnKysrIyMjJyMjKyMjFxcXGxcXGxsbHxcXHxsbEw8PExMTFxMTBwcHBwsLCwcG/v7+9vb2+vr6/vr67u7u8vLy5ubm6ubm7ubm3t7e4t7e4uLi1tbW3tra0s7OysbGysrKzsbGvr6+wr6+urq6vrq6sq6utrKypqamrqqqsqqqop6epp6eqqKimpaWnpaWnpqako6OkpKSlo6OioaGjoqKkoqKgn5+goKChn5+hoKCenp6fnp6cm5ucnJydm5udnJyenJx+fX1/fHx/fX2Afn6Af3+Bf3+BgICCgICCgYGDgYGDgoKEgoKFgoKFg4OGhISGhYWHhYWIh4eJh4eJiIiJiYmKiIiLiYmLioqMioqMi4uNi4uNjIyOjIyPjY2Qjo6Qj4+SkJCSkZGTkZGTkpKUk5OVk5OVlJSWlZWXlZWYlpaYl5eYmJiZmJiZmZmamJibmZmbmpqcm5uenp6fn5+op6erqqouFWZ5AAAAXHRSTlMADw8PFhYWHR0kJCwsLDMzMzo6OkJCSUlJUFBQV1dXX19fX19mZmZtbW11fHx8g4OKioqSkpKZmaCoqKivr7a2vb3FxcXMzMzT09Pb29vi4uLp6enp8PD4+Pj4+E8lS9YAAAEjSURBVCjPY2DACYRtrDhwywralTZL45RldymLb5LEJctkWBOXHiSAS9q8OSOzxRiXrGJPWmqdNTcOWY3ggtQyN15cXvIpTCnxVcIhK+pYnZwZLYdDls22Nj2rywiHLKNeW2p8oxaMK6vGgiKt05edVGXPDOWJB7S6KyPJSgVnxZRb8sC4Co2JJV4icFkJl6KYXB8hOJ8/KiW5wl8TyuN0KonP81BEMs24Iye10A8S+Kwm1bGZESoobtHszkwtDtUGMS0aUjM7TdG8YhRZmJIdLc/IpNqWktpoxonuVeWw3OTC3gkT+/NTK525MINCN6QsNSuvND+93FUGW1CJeZekJiSn5wSKYw9KNc/q9IS8cH1cCYDPoT63XR13ypQwczbAEAQAnkJCZAp/V+QAAAAASUVORK5CYII=')
;
background-repeat
:
no-repeat
;
background-size
:
16px
;
background-position
:
2px
4px
;
}
.dropdown-multi-menu
.dropdown-multi-menu-tip-area
{
margin
:
10px
;
text-align
:
left
;
max-height
:
40px
;
overflow-y
:
scroll
;
}
.dropdown-multi-menu
.dropdown-multi-menu-tip-area
p
{
margin-bottom
:
0
;
}
.dropdown-multi-menu
.input-with-icon
{
position
:
relative
;
margin
:
10px
0
;
}
.dropdown-multi-menu
.input-with-icon
i
{
position
:
absolute
;
right
:
0
;
padding
:
10px
12px
;
color
:
#817F7F
;
pointer-events
:
none
;
}
app/views/shared/issuable/_filter.html.haml
浏览文件 @
8dcd03f2
...
...
@@ -42,6 +42,11 @@
class:
'select2 trigger-submit'
,
include_blank:
true
,
data:
{
placeholder:
'Milestone'
})
.filter-item.inline.people-filter.btn-group
%button
.btn.btn-default.dropdown-toggle
{
"data-toggle"
=>
'dropdown'
}
People
%span
.caret
%ul
.dropdown-menu.dropdown-multi-menu
.filter-item.inline.labels-filter
=
select_tag
(
'label_name'
,
projects_labels_options
,
class:
'select2 trigger-submit'
,
include_blank:
true
,
...
...
@@ -70,3 +75,11 @@
event
.
preventDefault
();
Turbolinks
.
visit
(
this
.
action
+
'
&
'
+
$
(
this
).
serialize
());
});
$
(
'
ul.dropdown-multi-menu
'
).
multiawesome
({
data
:
[{
label
:
'
milestone
'
,
data
:
1
},{
label
:
'
no milestone
'
,
data
:
1
}],
title
:
"
Milestones
"
,
onChange
:
function
(
item
)
{
console
.
log
(
"
changed
"
,
item
)
}
});
db/schema.rb
已删除
100644 → 0
浏览文件 @
d33cc4e5
此差异已折叠。
点击以展开。
vendor/assets/javascripts/sifter.js
0 → 100644
浏览文件 @
8dcd03f2
/**
* sifter.js
* Copyright (c) 2013 Brian Reavis & contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
* file except in compliance with the License. You may obtain a copy of the License at:
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
* ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*
* @author Brian Reavis <brian@thirdroute.com>
*/
(
function
(
root
,
factory
)
{
if
(
typeof
define
===
'
function
'
&&
define
.
amd
)
{
define
(
factory
);
}
else
if
(
typeof
exports
===
'
object
'
)
{
module
.
exports
=
factory
();
}
else
{
root
.
Sifter
=
factory
();
}
}(
this
,
function
()
{
/**
* Textually searches arrays and hashes of objects
* by property (or multiple properties). Designed
* specifically for autocomplete.
*
* @constructor
* @param {array|object} items
* @param {object} items
*/
var
Sifter
=
function
(
items
,
settings
)
{
this
.
items
=
items
;
this
.
settings
=
settings
||
{
diacritics
:
true
};
};
/**
* Splits a search string into an array of individual
* regexps to be used to match results.
*
* @param {string} query
* @returns {array}
*/
Sifter
.
prototype
.
tokenize
=
function
(
query
)
{
query
=
trim
(
String
(
query
||
''
).
toLowerCase
());
if
(
!
query
||
!
query
.
length
)
return
[];
var
i
,
n
,
regex
,
letter
;
var
tokens
=
[];
var
words
=
query
.
split
(
/ +/
);
for
(
i
=
0
,
n
=
words
.
length
;
i
<
n
;
i
++
)
{
regex
=
escape_regex
(
words
[
i
]);
if
(
this
.
settings
.
diacritics
)
{
for
(
letter
in
DIACRITICS
)
{
if
(
DIACRITICS
.
hasOwnProperty
(
letter
))
{
regex
=
regex
.
replace
(
new
RegExp
(
letter
,
'
g
'
),
DIACRITICS
[
letter
]);
}
}
}
tokens
.
push
({
string
:
words
[
i
],
regex
:
new
RegExp
(
regex
,
'
i
'
)
});
}
return
tokens
;
};
/**
* Iterates over arrays and hashes.
*
* ```
* this.iterator(this.items, function(item, id) {
* // invoked for each item
* });
* ```
*
* @param {array|object} object
*/
Sifter
.
prototype
.
iterator
=
function
(
object
,
callback
)
{
var
iterator
;
if
(
is_array
(
object
))
{
iterator
=
Array
.
prototype
.
forEach
||
function
(
callback
)
{
for
(
var
i
=
0
,
n
=
this
.
length
;
i
<
n
;
i
++
)
{
callback
(
this
[
i
],
i
,
this
);
}
};
}
else
{
iterator
=
function
(
callback
)
{
for
(
var
key
in
this
)
{
if
(
this
.
hasOwnProperty
(
key
))
{
callback
(
this
[
key
],
key
,
this
);
}
}
};
}
iterator
.
apply
(
object
,
[
callback
]);
};
/**
* Returns a function to be used to score individual results.
*
* Good matches will have a higher score than poor matches.
* If an item is not a match, 0 will be returned by the function.
*
* @param {object|string} search
* @param {object} options (optional)
* @returns {function}
*/
Sifter
.
prototype
.
getScoreFunction
=
function
(
search
,
options
)
{
var
self
,
fields
,
tokens
,
token_count
;
self
=
this
;
search
=
self
.
prepareSearch
(
search
,
options
);
tokens
=
search
.
tokens
;
fields
=
search
.
options
.
fields
;
token_count
=
tokens
.
length
;
/**
* Calculates how close of a match the
* given value is against a search token.
*
* @param {mixed} value
* @param {object} token
* @return {number}
*/
var
scoreValue
=
function
(
value
,
token
)
{
var
score
,
pos
;
if
(
!
value
)
return
0
;
value
=
String
(
value
||
''
);
pos
=
value
.
search
(
token
.
regex
);
if
(
pos
===
-
1
)
return
0
;
score
=
token
.
string
.
length
/
value
.
length
;
if
(
pos
===
0
)
score
+=
0.5
;
return
score
;
};
/**
* Calculates the score of an object
* against the search query.
*
* @param {object} token
* @param {object} data
* @return {number}
*/
var
scoreObject
=
(
function
()
{
var
field_count
=
fields
.
length
;
if
(
!
field_count
)
{
return
function
()
{
return
0
;
};
}
if
(
field_count
===
1
)
{
return
function
(
token
,
data
)
{
return
scoreValue
(
data
[
fields
[
0
]],
token
);
};
}
return
function
(
token
,
data
)
{
for
(
var
i
=
0
,
sum
=
0
;
i
<
field_count
;
i
++
)
{
sum
+=
scoreValue
(
data
[
fields
[
i
]],
token
);
}
return
sum
/
field_count
;
};
})();
if
(
!
token_count
)
{
return
function
()
{
return
0
;
};
}
if
(
token_count
===
1
)
{
return
function
(
data
)
{
return
scoreObject
(
tokens
[
0
],
data
);
};
}
if
(
search
.
options
.
conjunction
===
'
and
'
)
{
return
function
(
data
)
{
var
score
;
for
(
var
i
=
0
,
sum
=
0
;
i
<
token_count
;
i
++
)
{
score
=
scoreObject
(
tokens
[
i
],
data
);
if
(
score
<=
0
)
return
0
;
sum
+=
score
;
}
return
sum
/
token_count
;
};
}
else
{
return
function
(
data
)
{
for
(
var
i
=
0
,
sum
=
0
;
i
<
token_count
;
i
++
)
{
sum
+=
scoreObject
(
tokens
[
i
],
data
);
}
return
sum
/
token_count
;
};
}
};
/**
* Returns a function that can be used to compare two
* results, for sorting purposes. If no sorting should
* be performed, `null` will be returned.
*
* @param {string|object} search
* @param {object} options
* @return function(a,b)
*/
Sifter
.
prototype
.
getSortFunction
=
function
(
search
,
options
)
{
var
i
,
n
,
self
,
field
,
fields
,
fields_count
,
multiplier
,
multipliers
,
get_field
,
implicit_score
,
sort
;
self
=
this
;
search
=
self
.
prepareSearch
(
search
,
options
);
sort
=
(
!
search
.
query
&&
options
.
sort_empty
)
||
options
.
sort
;
/**
* Fetches the specified sort field value
* from a search result item.
*
* @param {string} name
* @param {object} result
* @return {mixed}
*/
get_field
=
function
(
name
,
result
)
{
if
(
name
===
'
$score
'
)
return
result
.
score
;
return
self
.
items
[
result
.
id
][
name
];
};
// parse options
fields
=
[];
if
(
sort
)
{
for
(
i
=
0
,
n
=
sort
.
length
;
i
<
n
;
i
++
)
{
if
(
search
.
query
||
sort
[
i
].
field
!==
'
$score
'
)
{
fields
.
push
(
sort
[
i
]);
}
}
}
// the "$score" field is implied to be the primary
// sort field, unless it's manually specified
if
(
search
.
query
)
{
implicit_score
=
true
;
for
(
i
=
0
,
n
=
fields
.
length
;
i
<
n
;
i
++
)
{
if
(
fields
[
i
].
field
===
'
$score
'
)
{
implicit_score
=
false
;
break
;
}
}
if
(
implicit_score
)
{
fields
.
unshift
({
field
:
'
$score
'
,
direction
:
'
desc
'
});
}
}
else
{
for
(
i
=
0
,
n
=
fields
.
length
;
i
<
n
;
i
++
)
{
if
(
fields
[
i
].
field
===
'
$score
'
)
{
fields
.
splice
(
i
,
1
);
break
;
}
}
}
multipliers
=
[];
for
(
i
=
0
,
n
=
fields
.
length
;
i
<
n
;
i
++
)
{
multipliers
.
push
(
fields
[
i
].
direction
===
'
desc
'
?
-
1
:
1
);
}
// build function
fields_count
=
fields
.
length
;
if
(
!
fields_count
)
{
return
null
;
}
else
if
(
fields_count
===
1
)
{
field
=
fields
[
0
].
field
;
multiplier
=
multipliers
[
0
];
return
function
(
a
,
b
)
{
return
multiplier
*
cmp
(
get_field
(
field
,
a
),
get_field
(
field
,
b
)
);
};
}
else
{
return
function
(
a
,
b
)
{
var
i
,
result
,
a_value
,
b_value
,
field
;
for
(
i
=
0
;
i
<
fields_count
;
i
++
)
{
field
=
fields
[
i
].
field
;
result
=
multipliers
[
i
]
*
cmp
(
get_field
(
field
,
a
),
get_field
(
field
,
b
)
);
if
(
result
)
return
result
;
}
return
0
;
};
}
};
/**
* Parses a search query and returns an object
* with tokens and fields ready to be populated
* with results.
*
* @param {string} query
* @param {object} options
* @returns {object}
*/
Sifter
.
prototype
.
prepareSearch
=
function
(
query
,
options
)
{
if
(
typeof
query
===
'
object
'
)
return
query
;
options
=
extend
({},
options
);
var
option_fields
=
options
.
fields
;
var
option_sort
=
options
.
sort
;
var
option_sort_empty
=
options
.
sort_empty
;
if
(
option_fields
&&
!
is_array
(
option_fields
))
options
.
fields
=
[
option_fields
];
if
(
option_sort
&&
!
is_array
(
option_sort
))
options
.
sort
=
[
option_sort
];
if
(
option_sort_empty
&&
!
is_array
(
option_sort_empty
))
options
.
sort_empty
=
[
option_sort_empty
];
return
{
options
:
options
,
query
:
String
(
query
||
''
).
toLowerCase
(),
tokens
:
this
.
tokenize
(
query
),
total
:
0
,
items
:
[]
};
};
/**
* Searches through all items and returns a sorted array of matches.
*
* The `options` parameter can contain:
*
* - fields {string|array}
* - sort {array}
* - score {function}
* - filter {bool}
* - limit {integer}
*
* Returns an object containing:
*
* - options {object}
* - query {string}
* - tokens {array}
* - total {int}
* - items {array}
*
* @param {string} query
* @param {object} options
* @returns {object}
*/
Sifter
.
prototype
.
search
=
function
(
query
,
options
)
{
var
self
=
this
,
value
,
score
,
search
,
calculateScore
;
var
fn_sort
;
var
fn_score
;
search
=
this
.
prepareSearch
(
query
,
options
);
options
=
search
.
options
;
query
=
search
.
query
;
// generate result scoring function
fn_score
=
options
.
score
||
self
.
getScoreFunction
(
search
);
// perform search and sort
if
(
query
.
length
)
{
self
.
iterator
(
self
.
items
,
function
(
item
,
id
)
{
score
=
fn_score
(
item
);
if
(
options
.
filter
===
false
||
score
>
0
)
{
search
.
items
.
push
({
'
score
'
:
score
,
'
id
'
:
id
});
}
});
}
else
{
self
.
iterator
(
self
.
items
,
function
(
item
,
id
)
{
search
.
items
.
push
({
'
score
'
:
1
,
'
id
'
:
id
});
});
}
fn_sort
=
self
.
getSortFunction
(
search
,
options
);
if
(
fn_sort
)
search
.
items
.
sort
(
fn_sort
);
// apply limits
search
.
total
=
search
.
items
.
length
;
if
(
typeof
options
.
limit
===
'
number
'
)
{
search
.
items
=
search
.
items
.
slice
(
0
,
options
.
limit
);
}
return
search
;
};
// utilities
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
var
cmp
=
function
(
a
,
b
)
{
if
(
typeof
a
===
'
number
'
&&
typeof
b
===
'
number
'
)
{
return
a
>
b
?
1
:
(
a
<
b
?
-
1
:
0
);
}
a
=
asciifold
(
String
(
a
||
''
));
b
=
asciifold
(
String
(
b
||
''
));
if
(
a
>
b
)
return
1
;
if
(
b
>
a
)
return
-
1
;
return
0
;
};
var
extend
=
function
(
a
,
b
)
{
var
i
,
n
,
k
,
object
;
for
(
i
=
1
,
n
=
arguments
.
length
;
i
<
n
;
i
++
)
{
object
=
arguments
[
i
];
if
(
!
object
)
continue
;
for
(
k
in
object
)
{
if
(
object
.
hasOwnProperty
(
k
))
{
a
[
k
]
=
object
[
k
];
}
}
}
return
a
;
};
var
trim
=
function
(
str
)
{
return
(
str
+
''
).
replace
(
/^
\s
+|
\s
+$|/g
,
''
);
};
var
escape_regex
=
function
(
str
)
{
return
(
str
+
''
).
replace
(
/
([
.?*+^$[
\]\\
(){}|-
])
/g
,
'
\\
$1
'
);
};
var
is_array
=
Array
.
isArray
||
(
typeof
$
!==
'
undefined
'
&&
$
.
isArray
)
||
function
(
object
)
{
return
Object
.
prototype
.
toString
.
call
(
object
)
===
'
[object Array]
'
;
};
var
DIACRITICS
=
{
'
a
'
:
'
[aÀÁÂÃÄÅàáâãäåĀāąĄ]
'
,
'
c
'
:
'
[cÇçćĆčČ]
'
,
'
d
'
:
'
[dđĐďĎð]
'
,
'
e
'
:
'
[eÈÉÊËèéêëěĚĒēęĘ]
'
,
'
i
'
:
'
[iÌÍÎÏìíîïĪī]
'
,
'
l
'
:
'
[lłŁ]
'
,
'
n
'
:
'
[nÑñňŇńŃ]
'
,
'
o
'
:
'
[oÒÓÔÕÕÖØòóôõöøŌō]
'
,
'
r
'
:
'
[rřŘ]
'
,
'
s
'
:
'
[sŠšśŚ]
'
,
'
t
'
:
'
[tťŤ]
'
,
'
u
'
:
'
[uÙÚÛÜùúûüůŮŪū]
'
,
'
y
'
:
'
[yŸÿýÝ]
'
,
'
z
'
:
'
[zŽžżŻźŹ]
'
};
var
asciifold
=
(
function
()
{
var
i
,
n
,
k
,
chunk
;
var
foreignletters
=
''
;
var
lookup
=
{};
for
(
k
in
DIACRITICS
)
{
if
(
DIACRITICS
.
hasOwnProperty
(
k
))
{
chunk
=
DIACRITICS
[
k
].
substring
(
2
,
DIACRITICS
[
k
].
length
-
1
);
foreignletters
+=
chunk
;
for
(
i
=
0
,
n
=
chunk
.
length
;
i
<
n
;
i
++
)
{
lookup
[
chunk
.
charAt
(
i
)]
=
k
;
}
}
}
var
regexp
=
new
RegExp
(
'
[
'
+
foreignletters
+
'
]
'
,
'
g
'
);
return
function
(
str
)
{
return
str
.
replace
(
regexp
,
function
(
foreignletter
)
{
return
lookup
[
foreignletter
];
}).
toLowerCase
();
};
})();
// export
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
return
Sifter
;
}));
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录