Skip to content
Snippets Groups Projects
Commit 8f995295 authored by Peter Rotich's avatar Peter Rotich
Browse files

Merge branch 'feature/typeahead' into develop

parents 0040752d 20d904bc
Branches
Tags
No related merge requests found
......@@ -22,30 +22,32 @@ class TicketsAjaxAPI extends AjaxController {
function search() {
$limit = isset($_GET['limit']) ? (int) $_GET['limit']:25;
$limit = isset($_REQUEST['limit']) ? (int) $_REQUEST['limit']:25;
$items=array();
$ticketid=false;
if(isset($_GET['id'])){
$WHERE=' WHERE ticketID LIKE \''.db_input($_GET['id'], false).'%\'';
if(is_numeric($_REQUEST['q'])) {
$WHERE=' WHERE ticketID LIKE \''.db_input($_REQUEST['q'], false).'%\'';
$ticketid=true;
}elseif(isset($_GET['email'])){
$WHERE=' WHERE email LIKE \''.db_input(strtolower($_GET['email']), false).'%\'';
}else{
Http::response(400, "id or email argument is required");
} elseif(isset($_REQUEST['q'])) {
$WHERE=' WHERE email LIKE \''.db_input(strtolower($_REQUEST['q']), false).'%\'';
} else {
Http::response(400, 'Query argument is required');
}
$sql='SELECT DISTINCT ticketID,email,name FROM '.TICKET_TABLE.' '.$WHERE.' ORDER BY created LIMIT '.$limit;
$res=db_query($sql);
if($res && db_num_rows($res)){
$sql='SELECT DISTINCT ticketID, email, name '
.' FROM '.TICKET_TABLE.' '.$WHERE
.' ORDER BY created '
.' LIMIT '.$limit;
if(($res=db_query($sql)) && db_num_rows($res)){
while(list($id,$email,$name)=db_fetch_row($res)) {
$info=($ticketid)?$email:$id;
$id=($ticketid)?$id:$email;
# TODO: Return 'name' from email address if 'email' argument
# specified?
$items[] = array('id'=>$id, 'value'=>$id, 'info'=>$info,
'name'=>$name);
}
}
return $this->encode(array('results'=>$items));
return $this->encode($items);
}
function acquireLock($tid) {
......
......@@ -30,7 +30,7 @@ class Dispatcher {
if ($this->file) { $this->lazy_load(); }
# Support HTTP method emulation with the _method GET argument
if (isset($_GET['_method'])) {
$_SERVER['REQUEST_METHOD'] = strtoupper($_GET['method']);
$_SERVER['REQUEST_METHOD'] = strtoupper($_GET['_method']);
unset($_GET['_method']);
}
foreach ($this->urls as $matcher) {
......
......@@ -12,7 +12,7 @@ header("Content-Type: text/html; charset=UTF-8\r\n");
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<link rel="stylesheet" href="<?php echo ASSETS_PATH; ?>css/theme.css" media="screen">
<link rel="stylesheet" href="<?php echo ASSETS_PATH; ?>css/print.css" media="print">
<script src="./js/jquery.min.js"></script>
<script src="./js/jquery-1.7.2.min.js"></script>
<script src="./js/osticket.js"></script>
</head>
<body>
......
......@@ -8,12 +8,14 @@
.tip_shadow { display:block !important; }
</style>
<![endif]-->
<script type="text/javascript" src="../js/jquery.min.js"></script>
<script type="text/javascript" src="../js/jquery-1.7.2.min.js"></script>
<script type="text/javascript" src="./js/calendar.js"></script>
<script type="text/javascript" src="./js/tips.js"></script>
<script type="text/javascript" src="./js/nicEdit.js"></script>
<script type="text/javascript" src="./js/bootstrap-typeahead.js"></script>
<script type="text/javascript" src="./js/scp.js"></script>
<link rel="stylesheet" href="./css/scp.css" media="screen">
<link rel="stylesheet" href="./css/typeahead.css" media="screen">
</head>
<body>
<div id="container">
......
......@@ -254,7 +254,7 @@ $basic_display=!isset($_REQUEST['advance_search'])?true:false;
<input type="hidden" name="a" value="search">
<table>
<tr>
<td><input type="text" id="query" name="query" size=30 value="<?php echo Format::htmlchars($_REQUEST['query']); ?>"></td>
<td><input type="text" id="ticket-search" name="query" size=30 value="<?php echo Format::htmlchars($_REQUEST['query']); ?>"></td>
<td><input type="submit" name="basic_search" class="button" value="Search"></td>
</tr>
</table>
......
This diff is collapsed.
This diff is collapsed.
......@@ -45,7 +45,7 @@ $dispatcher = patterns("",
url("^/config/", patterns("ajax.config.php:ConfigAjaxAPI",
url_get("^ui",'ui')
)),
url_get("^/ticket$", array("ajax.tickets.php:TicketsAjaxAPI", "search")),
url_get("^/tickets$", array("ajax.tickets.php:TicketsAjaxAPI", "search")),
url("^/ticket/", patterns("ajax.tickets.php:TicketsAjaxAPI",
url_get("^(?P<tid>\d+)/preview", "previewTicket"),
url_get("^(?P<tid>\d+)/lock", "acquireLock"),
......
/* FROM bootstrap.css - just what is needed for typeahead */
.dropdown-menu {
position: absolute;
top: 100%;
left: 0;
z-index: 1000;
float: left;
display: none;
min-width: 160px;
padding: 4px 0;
margin: 0;
list-style: none;
background-color: #ffffff;
border-color: #ccc;
border-color: rgba(0, 0, 0, 0.2);
border-style: solid;
border-width: 1px;
-webkit-border-radius: 0 0 5px 5px;
-moz-border-radius: 0 0 5px 5px;
border-radius: 0 0 5px 5px;
-webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
-moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
-webkit-background-clip: padding-box;
-moz-background-clip: padding;
background-clip: padding-box;
*border-right-width: 2px;
*border-bottom-width: 2px;
}
.dropdown-menu.pull-right {
right: 0;
left: auto;
}
.dropdown-menu .divider {
height: 1px;
margin: 8px 1px;
overflow: hidden;
background-color: #e5e5e5;
border-bottom: 1px solid #ffffff;
*width: 100%;
*margin: -5px 0 5px;
}
.dropdown-menu a {
display: block;
padding: 3px 15px;
clear: both;
font-weight: normal;
line-height: 18px;
color: #333333;
white-space: nowrap;
}
.dropdown-menu li > a:hover,
.dropdown-menu .active > a,
.dropdown-menu .active > a:hover {
color: #ffffff;
text-decoration: none;
background-color: #0088cc;
}
/* =============================================================
* bootstrap-typeahead.js v2.0.0
* http://twitter.github.com/bootstrap/javascript.html#typeahead
* =============================================================
* Copyright 2012 Twitter, Inc.
*
* 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.
* ============================================================ */
!function( $ ){
"use strict"
var Typeahead = function ( element, options ) {
this.$element = $(element)
this.options = $.extend({}, $.fn.typeahead.defaults, options)
this.matcher = this.options.matcher || this.matcher
this.sorter = this.options.sorter || this.sorter
this.highlighter = this.options.highlighter || this.highlighter
this.$menu = $(this.options.menu).appendTo('body')
this.source = this.options.source
this.onselect = this.options.onselect
this.strings = true
this.shown = false
this.listen()
}
Typeahead.prototype = {
constructor: Typeahead
, select: function () {
var val = JSON.parse(this.$menu.find('.active').attr('data-value'))
, text
if (!this.strings) text = val[this.options.property]
else text = val
this.$element.val(text)
if (typeof this.onselect == "function")
this.onselect(val)
return this.hide()
}
, show: function () {
var pos = $.extend({}, this.$element.offset(), {
height: this.$element[0].offsetHeight
})
this.$menu.css({
top: pos.top + pos.height
, left: pos.left
})
this.$menu.show()
this.shown = true
return this
}
, hide: function () {
this.$menu.hide()
this.shown = false
return this
}
, lookup: function (event) {
var that = this
, items
, q
, value
this.query = this.$element.val();
/*Check if we have a match on the current source?? */
if (typeof this.source == "function") {
value = this.source(this, this.query)
if (value) this.process(value)
} else {
this.process(this.source)
}
}
, process: function (results) {
var that = this
, items
, q
if (results.length && typeof results[0] != "string")
this.strings = false
this.query = this.$element.val()
if (!this.query) {
return this.shown ? this.hide() : this
}
items = $.grep(results, function (item) {
if (!that.strings)
item = item[that.options.property]
if (that.matcher(item))
return item
})
items = this.sorter(items)
if (!items.length) {
return this.shown ? this.hide() : this
}
return this.render(items.slice(0, this.options.items)).show()
}
, matcher: function (item) {
return ~item.toLowerCase().indexOf(this.query.toLowerCase())
}
, sorter: function (items) {
var beginswith = []
, caseSensitive = []
, caseInsensitive = []
, item
, sortby
while (item = items.shift()) {
if (this.strings) sortby = item
else sortby = item[this.options.property]
if (!sortby.toLowerCase().indexOf(this.query.toLowerCase())) beginswith.push(item)
else if (~sortby.indexOf(this.query)) caseSensitive.push(item)
else caseInsensitive.push(item)
}
return beginswith.concat(caseSensitive, caseInsensitive)
}
, highlighter: function (item) {
return item.replace(new RegExp('(' + this.query + ')', 'ig'), function ($1, match) {
return '<strong>' + match + '</strong>'
})
}
, render: function (items) {
var that = this
items = $(items).map(function (i, item) {
i = $(that.options.item).attr('data-value', JSON.stringify(item))
if (!that.strings)
item = item[that.options.property]
i.find('a').html(that.highlighter(item))
return i[0]
})
items.first().addClass('active')
this.$menu.html(items)
return this
}
, next: function (event) {
var active = this.$menu.find('.active').removeClass('active')
, next = active.next()
if (!next.length) {
next = $(this.$menu.find('li')[0])
}
next.addClass('active')
}
, prev: function (event) {
var active = this.$menu.find('.active').removeClass('active')
, prev = active.prev()
if (!prev.length) {
prev = this.$menu.find('li').last()
}
prev.addClass('active')
}
, listen: function () {
this.$element
.on('blur', $.proxy(this.blur, this))
.on('keypress', $.proxy(this.keypress, this))
.on('keyup', $.proxy(this.keyup, this))
if ($.browser.webkit || $.browser.msie) {
this.$element.on('keydown', $.proxy(this.keypress, this))
}
this.$menu
.on('click', $.proxy(this.click, this))
.on('mouseenter', 'li', $.proxy(this.mouseenter, this))
}
, keyup: function (e) {
e.stopPropagation()
e.preventDefault()
switch(e.keyCode) {
case 40: // down arrow
case 38: // up arrow
break
case 9: // tab
case 13: // enter
if (!this.shown) return
this.select()
break
case 27: // escape
this.hide()
break
default:
this.lookup()
}
}
, keypress: function (e) {
e.stopPropagation()
if (!this.shown) return
switch(e.keyCode) {
case 9: // tab
case 13: // enter
case 27: // escape
e.preventDefault()
break
case 38: // up arrow
e.preventDefault()
this.prev()
break
case 40: // down arrow
e.preventDefault()
this.next()
break
}
}
, blur: function (e) {
var that = this
e.stopPropagation()
e.preventDefault()
setTimeout(function () { that.hide() }, 150)
}
, click: function (e) {
e.stopPropagation()
e.preventDefault()
this.select()
}
, mouseenter: function (e) {
this.$menu.find('.active').removeClass('active')
$(e.currentTarget).addClass('active')
}
}
/* TYPEAHEAD PLUGIN DEFINITION
* =========================== */
$.fn.typeahead = function ( option ) {
return this.each(function () {
var $this = $(this)
, data = $this.data('typeahead')
, options = typeof option == 'object' && option
if (!data) $this.data('typeahead', (data = new Typeahead(this, options)))
if (typeof option == 'string') data[option]()
})
}
$.fn.typeahead.defaults = {
source: []
, items: 8
, menu: '<ul class="typeahead dropdown-menu"></ul>'
, item: '<li><a href="#"></a></li>'
, onselect: null
, property: 'value'
}
$.fn.typeahead.Constructor = Typeahead
/* TYPEAHEAD DATA-API
* ================== */
$(function () {
$('body').on('focus.typeahead.data-api', '[data-provide="typeahead"]', function (e) {
var $this = $(this)
if ($this.data('typeahead')) return
e.preventDefault()
$this.typeahead($this.data())
})
})
}( window.jQuery );
......@@ -185,4 +185,21 @@ $(document).ready(function(){
}
}
/* Typeahead init */
$('#ticket-search').typeahead({
source: function (typeahead, query) {
$.ajax({
url: "ajax.php/tickets?q="+query,
dataType: 'json',
success: function (data) {
typeahead.process(data);
}
});
},
onselect: function (obj) {
$('#ticket-search').closest('form').submit();
},
property: "value"
});
});
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment