Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
flask-admin
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
JIRA
JIRA
Merge Requests
0
Merge Requests
0
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
Python-Dev
flask-admin
Commits
ee297aa3
Commit
ee297aa3
authored
Mar 22, 2012
by
Serge S. Koval
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Time, DateTime, Date widgets and fields, documentation update.
parent
d463725e
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
651 additions
and
14 deletions
+651
-14
TODO.txt
TODO.txt
+5
-0
quickstart.rst
doc/quickstart.rst
+4
-4
simple.py
examples/sqla/simple.py
+1
-0
sqlamodel.py
flask_adminex/ext/sqlamodel.py
+24
-5
form.py
flask_adminex/form.py
+78
-0
model.py
flask_adminex/model.py
+2
-2
datepicker.css
flask_adminex/static/css/datepicker.css
+141
-0
bootstrap-datepicker.js
flask_adminex/static/js/bootstrap-datepicker.js
+374
-0
form.js
flask_adminex/static/js/form.js
+6
-0
test.html
flask_adminex/static/js/test.html
+13
-0
edit.html
flask_adminex/templates/admin/model/edit.html
+3
-3
No files found.
TODO.txt
View file @
ee297aa3
- Core
- Right-side menu items (auth?)
- Pregenerate URLs for menu
- Conditional js include for forms or pages
- More form widgets (date time, time, etc)
- Model Admin
- Ability to sort by fields that are not visible?
- SQLA Model Admin
- Use of date time widgets
- Validation of the joins in the query
- Automatic joined load for foreign keys
- Built-in filtering support
- Many2Many support
- Ability to override form field types
- WYSIWYG editor support
- File admin
- Documentation
- Unit tests
...
...
doc/quickstart.rst
View file @
ee297aa3
...
...
@@ -8,7 +8,7 @@ Introduction
------------
While developing the library, I attempted to make it as flexible as possible. Developer should
not
patch a library
to achieve desired functionality.
not
monkey-patch anything
to achieve desired functionality.
Library uses one simple, but powerful concept - administrative pieces are built as classes with
view methods.
...
...
@@ -30,12 +30,12 @@ implementing reusable functional pieces that are highly customizable.
For example, Flask-AdminEx provides ready-to-use SQLAlchemy model interface. It is implemented as a
class which accepts two parameters: model and a database session. While it exposes some
class-level variables which change behavior of the interface (somewhat similar to django.contrib.admin),
nothing prohibits you from overriding form creation
or
database access methods or adding more views.
nothing prohibits you from overriding form creation
logic,
database access methods or adding more views.
Initialization
--------------
To start using
Admin
, you have to create `Admin` class instance and associate it with Flask application::
To start using
Flask-AdminEx
, you have to create `Admin` class instance and associate it with Flask application::
from flask import Flask
from flask.ext.adminex import Admin
...
...
@@ -47,7 +47,7 @@ To start using Admin, you have to create `Admin` class instance and associate it
app.run()
If you
will run this application and will
navigate to `http://localhost:5000/admin/ <http://localhost:5000/admin/>`_,
If you
start this application and
navigate to `http://localhost:5000/admin/ <http://localhost:5000/admin/>`_,
you should see empty "Home" page with a navigation bar on top.
You can change application name by passing `name` parameter to the `Admin` class constructor::
...
...
examples/sqla/simple.py
View file @
ee297aa3
...
...
@@ -31,6 +31,7 @@ class Post(db.Model):
id
=
db
.
Column
(
db
.
Integer
,
primary_key
=
True
)
title
=
db
.
Column
(
db
.
String
(
120
))
text
=
db
.
Column
(
db
.
Text
)
date
=
db
.
Column
(
db
.
DateTime
)
user_id
=
db
.
Column
(
db
.
Integer
,
db
.
ForeignKey
(
User
.
id
))
user
=
db
.
relationship
(
User
,
backref
=
'posts'
)
...
...
flask_adminex/ext/sqlamodel.py
View file @
ee297aa3
...
...
@@ -3,13 +3,14 @@ from sqlalchemy.orm.interfaces import MANYTOONE, ONETOMANY
from
sqlalchemy.orm.attributes
import
InstrumentedAttribute
from
sqlalchemy.sql.expression
import
desc
from
wtforms.ext.sqlalchemy.orm
import
model_form
,
ModelConverter
from
wtforms
import
fields
from
wtforms.ext.sqlalchemy.orm
import
model_form
,
converts
,
ModelConverter
from
wtforms.ext.sqlalchemy.fields
import
QuerySelectField
,
QuerySelectMultipleField
from
flask
import
flash
from
flask.ext.adminex.model
import
BaseModelView
from
flask.ext.adminex
.form
import
AdminF
orm
from
flask.ext.adminex
import
f
orm
class
AdminModelConverter
(
ModelConverter
):
...
...
@@ -40,13 +41,17 @@ class AdminModelConverter(ModelConverter):
return
self
.
view
.
session
.
query
(
remote_model
)
if
prop
.
direction
is
MANYTOONE
:
return
QuerySelectField
(
query_factory
=
query_factory
,
**
kwargs
)
return
QuerySelectField
(
query_factory
=
query_factory
,
widget
=
form
.
ChosenSelectWidget
(),
**
kwargs
)
elif
prop
.
direction
is
ONETOMANY
:
# Skip backrefs
if
not
local_column
.
foreign_keys
and
self
.
view
.
hide_backrefs
:
return
None
return
QuerySelectMultipleField
(
query_factory
=
query_factory
,
**
kwargs
)
return
QuerySelectMultipleField
(
query_factory
=
query_factory
,
widget
=
form
.
ChosenSelectWidget
(
multiple
=
True
),
**
kwargs
)
else
:
# Ignore pk/fk
if
isinstance
(
prop
,
ColumnProperty
):
...
...
@@ -58,6 +63,20 @@ class AdminModelConverter(ModelConverter):
return
super
(
AdminModelConverter
,
self
)
.
convert
(
model
,
mapper
,
prop
,
field_args
)
@
converts
(
'Date'
)
def
conv_date
(
self
,
field_args
,
**
kwargs
):
field_args
[
'widget'
]
=
form
.
DatePickerWidget
()
return
fields
.
DateField
(
**
field_args
)
@
converts
(
'DateTime'
)
def
conv_datetime
(
self
,
field_args
,
**
kwargs
):
field_args
[
'widget'
]
=
form
.
DateTimePickerWidget
()
return
fields
.
DateTimeField
(
**
field_args
)
@
converts
(
'Time'
)
def
conv_time
(
self
,
field_args
,
**
kwargs
):
return
form
.
TimeField
(
**
field_args
)
class
ModelView
(
BaseModelView
):
"""
...
...
@@ -151,7 +170,7 @@ class ModelView(BaseModelView):
Create form from the model.
"""
return
model_form
(
self
.
model
,
AdminForm
,
form
.
AdminForm
,
self
.
form_columns
,
field_args
=
self
.
form_args
,
converter
=
AdminModelConverter
(
self
))
...
...
flask_adminex/form.py
View file @
ee297aa3
import
time
import
datetime
from
flask.ext
import
wtf
from
wtforms
import
fields
,
widgets
class
AdminForm
(
wtf
.
Form
):
...
...
@@ -17,3 +21,77 @@ class AdminForm(wtf.Form):
return
True
return
False
class
TimeField
(
fields
.
Field
):
"""
A text field which stores a `datetime.time` matching a format.
"""
widget
=
widgets
.
TextInput
()
def
__init__
(
self
,
label
=
None
,
validators
=
None
,
formats
=
None
,
**
kwargs
):
"""
Constructor
`label`
Label
`validators`
Field validators
`formats`
Supported time formats, as a enumerable.
`kwargs`
Any additional parameters
"""
super
(
TimeField
,
self
)
.
__init__
(
label
,
validators
,
**
kwargs
)
self
.
format
=
formats
or
(
'
%
H:
%
M:
%
S'
,
'
%
H:
%
M'
,
'
%
I:
%
M:
%
S
%
p'
,
'
%
I:
%
M
%
p'
,
'
%
I:
%
M:
%
S
%
p'
,
'
%
I:
%
M
%
p'
)
def
_value
(
self
):
if
self
.
raw_data
:
return
u' '
.
join
(
self
.
raw_data
)
else
:
return
self
.
data
and
self
.
data
.
strftime
(
self
.
format
)
or
u''
def
process_formdata
(
self
,
valuelist
):
if
valuelist
:
date_str
=
u' '
.
join
(
valuelist
)
for
format
in
self
.
formats
:
try
:
timetuple
=
time
.
strptime
(
date_str
,
format
)
self
.
data
=
datetime
.
time
(
timetuple
.
tm_hour
,
timetuple
.
tm_min
,
timetuple
.
tm_sec
)
return
except
ValueError
:
pass
raise
ValueError
(
'Invalid time format'
)
class
ChosenSelectWidget
(
widgets
.
Select
):
def
__call__
(
self
,
field
,
**
kwargs
):
if
field
.
allow_blank
and
not
self
.
multiple
:
kwargs
[
'data-role'
]
=
u'chosenblank'
else
:
kwargs
[
'data-role'
]
=
u'chosen'
return
super
(
ChosenSelectWidget
,
self
)
.
__call__
(
field
,
**
kwargs
)
class
ChosenSelectField
(
fields
.
SelectField
):
widget
=
ChosenSelectWidget
class
DatePickerWidget
(
widgets
.
TextInput
):
def
__call__
(
self
,
field
,
**
kwargs
):
kwargs
[
'data-role'
]
=
u'datepicker'
return
super
(
DatePickerWidget
,
self
)
.
__call__
(
field
,
**
kwargs
)
class
DateTimePickerWidget
(
widgets
.
TextInput
):
def
__call__
(
self
,
field
,
**
kwargs
):
kwargs
[
'data-role'
]
=
u'datetimepicker'
return
super
(
DateTimePickerWidget
,
self
)
.
__call__
(
field
,
**
kwargs
)
flask_adminex/model.py
View file @
ee297aa3
...
...
@@ -98,7 +98,7 @@ class BaseModelView(BaseView):
form_args
=
None
"""
Dictionary of form field arguments. Refer to WTForm documentation
on
Dictionary of form field arguments. Refer to WTForm documentation
for
list of possible options.
Example::
...
...
@@ -106,7 +106,7 @@ class BaseModelView(BaseView):
class MyModelView(BaseModelView):
form_args = dict(
name=dict(label='First Name', validators=[wtf.required()])
}
)
"""
# Various settings
...
...
flask_adminex/static/css/datepicker.css
0 → 100644
View file @
ee297aa3
.datepicker
{
background-color
:
#ffffff
;
border-color
:
#999
;
border-color
:
rgba
(
0
,
0
,
0
,
0.2
);
border-style
:
solid
;
border-width
:
1px
;
-webkit-border-radius
:
4px
;
-moz-border-radius
:
4px
;
border-radius
:
4px
;
-webkit-box-shadow
:
0
2px
4px
rgba
(
0
,
0
,
0
,
0.2
);
-moz-box-shadow
:
0
2px
4px
rgba
(
0
,
0
,
0
,
0.2
);
box-shadow
:
0
2px
4px
rgba
(
0
,
0
,
0
,
0.2
);
-webkit-background-clip
:
padding-box
;
-moz-background-clip
:
padding-box
;
background-clip
:
padding-box
;
display
:
none
;
position
:
absolute
;
z-index
:
900
;
margin-left
:
0
;
margin-right
:
0
;
margin-bottom
:
18px
;
padding-bottom
:
4px
;
width
:
218px
;
}
.datepicker
.nav
{
font-weight
:
bold
;
width
:
100%
;
padding
:
4px
0
;
background-color
:
#f5f5f5
;
color
:
#808080
;
border-bottom
:
1px
solid
#ddd
;
-webkit-box-shadow
:
inset
0
1px
0
#ffffff
;
-moz-box-shadow
:
inset
0
1px
0
#ffffff
;
box-shadow
:
inset
0
1px
0
#ffffff
;
zoom
:
1
;
}
.datepicker
.nav
:before
,
.datepicker
.nav
:after
{
display
:
table
;
content
:
""
;
zoom
:
1
;
*
display
:
inline
;
}
.datepicker
.nav
:after
{
clear
:
both
;
}
.datepicker
.nav
span
{
display
:
block
;
float
:
left
;
text-align
:
center
;
height
:
28px
;
line-height
:
28px
;
position
:
relative
;
}
.datepicker
.nav
.bg
{
width
:
100%
;
background-color
:
#fdf5d9
;
height
:
28px
;
position
:
absolute
;
top
:
0
;
left
:
0
;
-webkit-border-radius
:
4px
;
-moz-border-radius
:
4px
;
border-radius
:
4px
;
}
.datepicker
.nav
.fg
{
width
:
100%
;
position
:
absolute
;
top
:
0
;
left
:
0
;
}
.datepicker
.button
{
cursor
:
pointer
;
padding
:
0
4px
;
-webkit-border-radius
:
4px
;
-moz-border-radius
:
4px
;
border-radius
:
4px
;
}
.datepicker
.button
:hover
{
background-color
:
#808080
;
color
:
#ffffff
;
}
.datepicker
.months
{
float
:
left
;
margin-left
:
4px
;
}
.datepicker
.months
.name
{
width
:
72px
;
padding
:
0
;
}
.datepicker
.years
{
float
:
right
;
margin-right
:
4px
;
}
.datepicker
.years
.name
{
width
:
36px
;
padding
:
0
;
}
.datepicker
.dow
,
.datepicker
.days
div
{
float
:
left
;
width
:
30px
;
line-height
:
25px
;
text-align
:
center
;
}
.datepicker
.dow
{
font-weight
:
bold
;
color
:
#808080
;
}
.datepicker
.calendar
{
padding
:
4px
;
}
.datepicker
.days
div
{
cursor
:
pointer
;
-webkit-border-radius
:
4px
;
-moz-border-radius
:
4px
;
border-radius
:
4px
;
}
.datepicker
.days
div
:hover
{
background-color
:
#0064cd
;
color
:
#ffffff
;
}
.datepicker
.overlap
{
color
:
#bfbfbf
;
}
.datepicker
.today
{
background-color
:
#fee9cc
;
}
.datepicker
.selected
{
background-color
:
#bfbfbf
;
color
:
#ffffff
;
}
.datepicker
.time
{
clear
:
both
;
padding-top
:
8px
;
margin-left
:
4px
;
}
.datepicker
.time
label
{
text-align
:
center
;
}
.datepicker
.time
input
{
width
:
200px
;
}
flask_adminex/static/js/bootstrap-datepicker.js
0 → 100644
View file @
ee297aa3
/* ===========================================================
* bootstrap-datepicker.js v1.3.0
* http://twitter.github.com/bootstrap/javascript.html#datepicker
* ===========================================================
* Copyright 2011 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.
*
* Contributed by Scott Torborg - github.com/storborg
* Loosely based on jquery.date_input.js by Jon Leighton, heavily updated and
* rewritten to match bootstrap javascript approach and add UI features.
*
* Enhancements and fixes by Serge S. Koval - github.com/mrjoes
*
* =========================================================== */
!
function
(
$
)
{
var
all
=
[];
function
clearDatePickers
(
except
)
{
var
ii
;
for
(
ii
=
0
;
ii
<
all
.
length
;
ii
++
)
{
if
(
all
[
ii
]
!=
except
)
{
all
[
ii
].
hide
();
}
}
}
function
DatePicker
(
element
,
options
)
{
this
.
$el
=
$
(
element
);
this
.
proxy
(
'show'
).
proxy
(
'ahead'
).
proxy
(
'hide'
).
proxy
(
'keyHandler'
).
proxy
(
'selectDate'
);
var
options
=
$
.
extend
({},
$
.
fn
.
datepicker
.
defaults
,
options
);
// If custom parse/format function was not passed in options and time-based display
// is enabled, use time-compatible parse/format functions
if
(
options
.
displayTime
&&
!!!
options
.
parse
&&
!!!
options
.
format
)
{
options
.
parse
=
this
.
parseDateTime
;
}
$
.
extend
(
this
,
options
);
this
.
$el
.
data
(
'datepicker'
,
this
);
all
.
push
(
this
);
this
.
init
();
}
DatePicker
.
prototype
=
{
init
:
function
()
{
var
$months
=
this
.
nav
(
'months'
,
1
);
var
$years
=
this
.
nav
(
'years'
,
12
);
var
$nav
=
$
(
'<div>'
).
addClass
(
'nav'
).
append
(
$months
,
$years
);
this
.
$month
=
$
(
'.name'
,
$months
);
this
.
$year
=
$
(
'.name'
,
$years
);
$calendar
=
$
(
"<div>"
).
addClass
(
'calendar'
);
// Populate day of week headers, realigned by startOfWeek.
for
(
var
i
=
0
;
i
<
this
.
shortDayNames
.
length
;
i
++
)
{
$calendar
.
append
(
'<div class="dow">'
+
this
.
shortDayNames
[(
i
+
this
.
startOfWeek
)
%
7
]
+
'</div>'
);
};
this
.
$days
=
$
(
'<div>'
).
addClass
(
'days'
);
$calendar
.
append
(
this
.
$days
);
this
.
$picker
=
$
(
'<div>'
)
.
addClass
(
'datepicker'
)
.
append
(
$nav
,
$calendar
)
.
insertAfter
(
this
.
$el
);
this
.
$picker
.
children
()
.
click
(
function
(
e
)
{
e
.
stopPropagation
()
})
// Use this to prevent accidental text selection.
.
mousedown
(
function
(
e
)
{
e
.
preventDefault
()
});
if
(
this
.
displayTime
)
{
this
.
$time
=
$
(
'<div class="time">'
)
.
append
(
'<label for="_time">Time</label>'
)
.
append
(
'<input id="_time" type="text" />'
)
.
click
(
function
(
e
)
{
e
.
stopPropagation
();
});
this
.
$picker
.
append
(
this
.
$time
);
}
this
.
$el
.
focus
(
this
.
show
)
.
click
(
this
.
show
)
.
change
(
$
.
proxy
(
function
()
{
this
.
selectDate
();
},
this
));
this
.
selectDate
();
this
.
hide
();
}
,
nav
:
function
(
c
,
months
)
{
var
$subnav
=
$
(
'<div>'
+
'<span class="prev button">←</span>'
+
'<span class="name"></span>'
+
'<span class="next button">→</span>'
+
'</div>'
).
addClass
(
c
)
$
(
'.prev'
,
$subnav
).
click
(
$
.
proxy
(
function
()
{
this
.
ahead
(
-
months
,
0
)
},
this
));
$
(
'.next'
,
$subnav
).
click
(
$
.
proxy
(
function
()
{
this
.
ahead
(
months
,
0
)
},
this
));
return
$subnav
;
}
,
updateName
:
function
(
$area
,
s
)
{
// Update either the month or year field, with a background flash
// animation.
var
cur
=
$area
.
find
(
'.fg'
).
text
(),
$fg
=
$
(
'<div>'
).
addClass
(
'fg'
).
append
(
s
);
$area
.
empty
();
if
(
cur
!=
s
)
{
var
$bg
=
$
(
'<div>'
).
addClass
(
'bg'
);
$area
.
append
(
$bg
,
$fg
);
$bg
.
fadeOut
(
'slow'
,
function
()
{
$
(
this
).
remove
();
});
}
else
{
$area
.
append
(
$fg
);
}
}
,
selectMonth
:
function
(
date
)
{
this
.
displayedDate
=
date
;
var
newMonth
=
new
Date
(
date
.
getFullYear
(),
date
.
getMonth
(),
1
);
if
(
!
this
.
curMonth
||
!
(
this
.
curMonth
.
getFullYear
()
==
newMonth
.
getFullYear
()
&&
this
.
curMonth
.
getMonth
()
==
newMonth
.
getMonth
()))
{
this
.
curMonth
=
newMonth
;
var
rangeStart
=
this
.
rangeStart
(
date
),
rangeEnd
=
this
.
rangeEnd
(
date
);
var
num_days
=
this
.
daysBetween
(
rangeStart
,
rangeEnd
);
this
.
$days
.
empty
();
for
(
var
ii
=
0
;
ii
<=
num_days
;
ii
++
)
{
var
thisDay
=
new
Date
(
rangeStart
.
getFullYear
(),
rangeStart
.
getMonth
(),
rangeStart
.
getDate
()
+
ii
,
12
,
00
);
var
$day
=
$
(
'<div>'
).
attr
(
'date'
,
this
.
format
(
thisDay
));
$day
.
text
(
thisDay
.
getDate
());
if
(
thisDay
.
getMonth
()
!=
date
.
getMonth
())
{
$day
.
addClass
(
'overlap'
);
};
this
.
$days
.
append
(
$day
);
};
this
.
updateName
(
this
.
$month
,
this
.
monthNames
[
date
.
getMonth
()]);
this
.
updateName
(
this
.
$year
,
this
.
curMonth
.
getFullYear
());
$
(
'div'
,
this
.
$days
).
click
(
$
.
proxy
(
function
(
e
)
{
var
$targ
=
$
(
e
.
target
);
// The date= attribute is used here to provide relatively fast
// selectors for setting certain date cells.
this
.
update
(
$targ
.
attr
(
"date"
));
// Don't consider this selection final if we're just going to an
// adjacent month.
if
(
!
$targ
.
hasClass
(
'overlap'
))
{
this
.
hide
();
}
},
this
));
$
(
"[date='"
+
this
.
format
(
new
Date
())
+
"']"
,
this
.
$days
).
addClass
(
'today'
);
};
$
(
'.selected'
,
this
.
$days
).
removeClass
(
'selected'
);
$
(
'[date="'
+
this
.
selectedDateStr
+
'"]'
,
this
.
$days
).
addClass
(
'selected'
);
}
,
selectDate
:
function
(
date
)
{
if
(
typeof
(
date
)
==
"undefined"
)
{
date
=
this
.
parse
(
this
.
$el
.
val
());
};
if
(
!
date
)
{
date
=
new
Date
();
if
(
this
.
displayTime
)
{
date
=
new
Date
(
date
.
getFullYear
(),
date
.
getMonth
(),
date
.
getDate
());
}
}
this
.
selectedDate
=
date
;
this
.
displayedDate
=
date
;
this
.
selectedDateStr
=
this
.
format
(
this
.
selectedDate
);
this
.
selectMonth
(
this
.
selectedDate
);
if
(
this
.
displayTime
)
{
$
(
'input'
,
this
.
$time
).
val
(
this
.
formatTime
(
date
));
}
}
,
update
:
function
(
s
)
{
if
(
this
.
displayTime
)
s
=
s
+
' '
+
this
.
getTimeString
()
this
.
$el
.
val
(
s
).
change
();
}
,
show
:
function
(
e
)
{
e
&&
e
.
stopPropagation
();
// Hide all other datepickers.
clearDatePickers
(
this
);
this
.
selectDate
();
var
offset
=
this
.
$el
.
offset
();
this
.
$picker
.
css
({
top
:
offset
.
top
+
this
.
$el
.
outerHeight
()
+
2
,
left
:
offset
.
left
}).
show
();
$
(
'html'
).
on
(
'keydown'
,
this
.
keyHandler
);
}
,
hide
:
function
()
{
this
.
$picker
.
hide
();
$
(
'html'
).
off
(
'keydown'
,
this
.
keyHandler
);
}
,
keyHandler
:
function
(
e
)
{
// Keyboard navigation shortcuts.
switch
(
e
.
keyCode
)
{
case
9
:
case
27
:
// Tab or escape hides the datepicker. In this case, just return
// instead of breaking, so that the e doesn't get stopped.
this
.
hide
();
return
;
case
13
:
// Enter selects the currently highlighted date.
this
.
update
(
this
.
selectedDateStr
);
this
.
hide
();
break
;
default
:
return
;
}
e
.
preventDefault
();
}
,
getTimeString
:
function
(
s
)
{
var
time
=
$
(
'input'
,
this
.
$time
).
val
();
return
this
.
validateTime
(
time
)
?
time
:
'12:00:00'
;
}
,
pad
:
function
(
s
)
{
if
(
s
.
length
===
1
)
s
=
'0'
+
s
;
return
s
;
}
,
parse
:
function
(
s
)
{
// Parse a partial RFC 3339 string into a Date.
var
m
;
if
((
m
=
s
.
match
(
/^
(\d{4,4})
-
(\d{2,2})
-
(\d{2,2})
$/
)))
{
return
new
Date
(
m
[
1
],
m
[
2
]
-
1
,
m
[
3
]);
}
else
{
return
null
;
}
}
,
parseDateTime
:
function
(
s
)
{
// Parse a partial RFC 3339 string into a Date.
var
m
;
if
((
m
=
s
.
match
(
/^
(\d{4,4})
-
(\d{2,2})
-
(\d{2,2})
(\d{2,2})
:
(\d{2,2})
:
(\d{2,2})
$/
)))
{
return
new
Date
(
m
[
1
],
m
[
2
]
-
1
,
m
[
3
],
m
[
4
],
m
[
5
],
m
[
6
]);
}
else
{
return
null
;
}
}
,
validateTime
:
function
(
s
)
{
return
s
.
match
(
/^
(\d{2,2})
:
(\d{2,2})
:
(\d{2,2})
$/
);
}
,
format
:
function
(
date
)
{
// Format a Date into a string as specified by RFC 3339.
var
month
=
this
.
pad
((
date
.
getMonth
()
+
1
).
toString
()),
dom
=
this
.
pad
(
date
.
getDate
().
toString
());
return
date
.
getFullYear
()
+
'-'
+
month
+
"-"
+
dom
;
}
,
formatTime
:
function
(
date
)
{
var
hour
=
this
.
pad
(
date
.
getHours
().
toString
()),
min
=
this
.
pad
(
date
.
getMinutes
().
toString
()),
sec
=
this
.
pad
(
date
.
getSeconds
().
toString
());
return
hour
+
':'
+
min
+
':'
+
sec
;
}
,
ahead
:
function
(
months
,
days
)
{
// Move ahead ``months`` months and ``days`` days, both integers, can be
// negative.
this
.
selectMonth
(
new
Date
(
this
.
displayedDate
.
getFullYear
(),
this
.
displayedDate
.
getMonth
()
+
months
,
this
.
displayedDate
.
getDate
()
+
days
));
}
,
proxy
:
function
(
meth
)
{
// Bind a method so that it always gets the datepicker instance for
// ``this``. Return ``this`` so chaining calls works.
this
[
meth
]
=
$
.
proxy
(
this
[
meth
],
this
);
return
this
;
}
,
daysBetween
:
function
(
start
,
end
)
{
// Return number of days between ``start`` Date object and ``end``.
var
start
=
Date
.
UTC
(
start
.
getFullYear
(),
start
.
getMonth
(),
start
.
getDate
());
var
end
=
Date
.
UTC
(
end
.
getFullYear
(),
end
.
getMonth
(),
end
.
getDate
());
return
(
end
-
start
)
/
86400000
;
}
,
findClosest
:
function
(
dow
,
date
,
direction
)
{
// From a starting date, find the first day ahead of behind it that is
// a given day of the week.
var
difference
=
direction
*
(
Math
.
abs
(
date
.
getDay
()
-
dow
-
(
direction
*
7
))
%
7
);
return
new
Date
(
date
.
getFullYear
(),
date
.
getMonth
(),
date
.
getDate
()
+
difference
);
}
,
rangeStart
:
function
(
date
)
{
// Get the first day to show in the current calendar view.
return
this
.
findClosest
(
this
.
startOfWeek
,
new
Date
(
date
.
getFullYear
(),
date
.
getMonth
()),
-
1
);
}
,
rangeEnd
:
function
(
date
)
{
// Get the last day to show in the current calendar view.
return
this
.
findClosest
((
this
.
startOfWeek
-
1
)
%
7
,
new
Date
(
date
.
getFullYear
(),
date
.
getMonth
()
+
1
,
0
),
1
);
}
};
/* DATEPICKER PLUGIN DEFINITION
* ============================ */
$
.
fn
.
datepicker
=
function
(
options
)
{
return
this
.
each
(
function
()
{
new
DatePicker
(
this
,
options
);
});
};
$
(
function
()
{
$
(
'html'
).
click
(
clearDatePickers
);
});
$
.
fn
.
datepicker
.
DatePicker
=
DatePicker
;
$
.
fn
.
datepicker
.
defaults
=
{
monthNames
:
[
"January"
,
"February"
,
"March"
,
"April"
,
"May"
,
"June"
,
"July"
,
"August"
,
"September"
,
"October"
,
"November"
,
"December"
]
,
shortDayNames
:
[
"Sun"
,
"Mon"
,
"Tue"
,
"Wed"
,
"Thu"
,
"Fri"
,
"Sat"
]
,
startOfWeek
:
1
,
displayTime
:
false
};
}(
window
.
jQuery
||
window
.
ender
);
flask_adminex/static/js/form.js
0 → 100644
View file @
ee297aa3
$
(
function
()
{
$
(
'[data-role=chosen]'
).
chosen
();
$
(
'[data-role=chosenblank]'
).
chosen
({
allow_single_deselect
:
true
});
$
(
'[data-role=datepicker]'
).
datepicker
();
$
(
'[data-role=datetimepicker]'
).
datepicker
({
displayTime
:
true
});
});
flask_adminex/static/js/test.html
0 → 100644
View file @
ee297aa3
<html>
<head>
<link
href=
"../bootstrap/css/bootstrap.css"
rel=
"stylesheet"
>
<link
href=
"../css/datepicker.css"
rel=
"stylesheet"
>
</head>
<body>
<form
action=
""
>
<input
data-datepicker=
"datepicker"
type=
"text"
value=
"2011-10-12"
/>
</form>
<script
src=
"http://code.jquery.com/jquery-1.7.min.js"
></script>
<script
src=
"bootstrap-datepicker.js"
></script>
</body>
</html>
\ No newline at end of file
flask_adminex/templates/admin/model/edit.html
View file @
ee297aa3
...
...
@@ -2,6 +2,7 @@
{% block head %}
<link
href=
"{{ url_for('admin.static', filename='chosen/chosen.css') }}"
rel=
"stylesheet"
>
<link
href=
"{{ url_for('admin.static', filename='css/datepicker.css') }}"
rel=
"stylesheet"
>
{% endblock %}
{% block body %}
...
...
@@ -37,7 +38,6 @@
{% endblock %}
{% block tail %}
<script>
$
(
"select"
).
chosen
({
allow_single_deselect
:
true
});
</script>
<script
src=
"{{ url_for('admin.static', filename='js/bootstrap-datepicker.js') }}"
></script>
<script
src=
"{{ url_for('admin.static', filename='js/form.js') }}"
></script>
{% endblock %}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment