Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
Y
yii2
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
PSDI Army
yii2
Commits
d6d48de9
Commit
d6d48de9
authored
Jun 04, 2014
by
Carsten Brandt
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #3685 from kartik-v/3196-masked-input-revamped
Fix #3196: Masked input widget revamped to use new plugin
parents
05e63b9b
b5461a76
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
94 additions
and
388 deletions
+94
-388
CHANGELOG.md
framework/CHANGELOG.md
+1
-0
jquery.inputmask.bundle.min.js
framework/assets/jquery.inputmask.bundle.min.js
+0
-0
jquery.maskedinput.js
framework/assets/jquery.maskedinput.js
+0
-338
MaskedInput.php
framework/widgets/MaskedInput.php
+87
-43
MaskedInputAsset.php
framework/widgets/MaskedInputAsset.php
+6
-7
No files found.
framework/CHANGELOG.md
View file @
d6d48de9
...
...
@@ -51,6 +51,7 @@ Yii Framework 2 Change Log
-
Enh #3108: Added
`yii\debug\Module::enableDebugLogs`
to disable logging debug logs by default (qiangxue)
-
Enh #3132:
`yii\rbac\PhpManager`
now supports more compact data file format (qiangxue)
-
Enh #3154: Added validation error display for
`GridView`
filters (ivan-kolmychek)
-
Enh #3196: Masked input upgraded to use jquery.inputmask plugin with more features. (kartik-v)
-
Enh #3222: Added
`useTablePrefix`
option to the model generator for Gii (horizons2)
-
Enh #3230: Added
`yii\filters\AccessControl::user`
to support access control with different actors (qiangxue)
-
Enh #3232: Added
`export()`
and
`exportAsString()`
methods to
`yii\helpers\BaseVarDumper`
(klimov-paul)
...
...
framework/assets/jquery.inputmask.bundle.min.js
0 → 100644
View file @
d6d48de9
This diff is collapsed.
Click to expand it.
framework/assets/jquery.maskedinput.js
deleted
100644 → 0
View file @
05e63b9b
/*
Masked Input plugin for jQuery
Copyright (c) 2007-2013 Josh Bush (digitalbush.com)
Licensed under the MIT license (http://digitalbush.com/projects/masked-input-plugin/#license)
Version: 1.3.1
*/
(
function
(
$
)
{
function
getPasteEvent
()
{
var
el
=
document
.
createElement
(
'input'
),
name
=
'onpaste'
;
el
.
setAttribute
(
name
,
''
);
return
(
typeof
el
[
name
]
===
'function'
)?
'paste'
:
'input'
;
}
var
pasteEventName
=
getPasteEvent
()
+
".mask"
,
ua
=
navigator
.
userAgent
,
iPhone
=
/iphone/i
.
test
(
ua
),
android
=
/android/i
.
test
(
ua
),
caretTimeoutId
;
$
.
mask
=
{
//Predefined character definitions
definitions
:
{
'9'
:
"[0-9]"
,
'a'
:
"[A-Za-z]"
,
'*'
:
"[A-Za-z0-9]"
},
dataName
:
"rawMaskFn"
,
placeholder
:
'_'
,
};
$
.
fn
.
extend
({
//Helper Function for Caret positioning
caret
:
function
(
begin
,
end
)
{
var
range
;
if
(
this
.
length
===
0
||
this
.
is
(
":hidden"
))
{
return
;
}
if
(
typeof
begin
==
'number'
)
{
end
=
(
typeof
end
===
'number'
)
?
end
:
begin
;
return
this
.
each
(
function
()
{
if
(
this
.
setSelectionRange
)
{
this
.
setSelectionRange
(
begin
,
end
);
}
else
if
(
this
.
createTextRange
)
{
range
=
this
.
createTextRange
();
range
.
collapse
(
true
);
range
.
moveEnd
(
'character'
,
end
);
range
.
moveStart
(
'character'
,
begin
);
range
.
select
();
}
});
}
else
{
if
(
this
[
0
].
setSelectionRange
)
{
begin
=
this
[
0
].
selectionStart
;
end
=
this
[
0
].
selectionEnd
;
}
else
if
(
document
.
selection
&&
document
.
selection
.
createRange
)
{
range
=
document
.
selection
.
createRange
();
begin
=
0
-
range
.
duplicate
().
moveStart
(
'character'
,
-
100000
);
end
=
begin
+
range
.
text
.
length
;
}
return
{
begin
:
begin
,
end
:
end
};
}
},
unmask
:
function
()
{
return
this
.
trigger
(
"unmask"
);
},
mask
:
function
(
mask
,
settings
)
{
var
input
,
defs
,
tests
,
partialPosition
,
firstNonMaskPos
,
len
;
if
(
!
mask
&&
this
.
length
>
0
)
{
input
=
$
(
this
[
0
]);
return
input
.
data
(
$
.
mask
.
dataName
)();
}
settings
=
$
.
extend
({
placeholder
:
$
.
mask
.
placeholder
,
// Load default placeholder
completed
:
null
},
settings
);
defs
=
$
.
mask
.
definitions
;
tests
=
[];
partialPosition
=
len
=
mask
.
length
;
firstNonMaskPos
=
null
;
$
.
each
(
mask
.
split
(
""
),
function
(
i
,
c
)
{
if
(
c
==
'?'
)
{
len
--
;
partialPosition
=
i
;
}
else
if
(
defs
[
c
])
{
tests
.
push
(
new
RegExp
(
defs
[
c
]));
if
(
firstNonMaskPos
===
null
)
{
firstNonMaskPos
=
tests
.
length
-
1
;
}
}
else
{
tests
.
push
(
null
);
}
});
return
this
.
trigger
(
"unmask"
).
each
(
function
()
{
var
input
=
$
(
this
),
buffer
=
$
.
map
(
mask
.
split
(
""
),
function
(
c
,
i
)
{
if
(
c
!=
'?'
)
{
return
defs
[
c
]
?
settings
.
placeholder
:
c
;
}
}),
focusText
=
input
.
val
();
function
seekNext
(
pos
)
{
while
(
++
pos
<
len
&&
!
tests
[
pos
]);
return
pos
;
}
function
seekPrev
(
pos
)
{
while
(
--
pos
>=
0
&&
!
tests
[
pos
]);
return
pos
;
}
function
shiftL
(
begin
,
end
)
{
var
i
,
j
;
if
(
begin
<
0
)
{
return
;
}
for
(
i
=
begin
,
j
=
seekNext
(
end
);
i
<
len
;
i
++
)
{
if
(
tests
[
i
])
{
if
(
j
<
len
&&
tests
[
i
].
test
(
buffer
[
j
]))
{
buffer
[
i
]
=
buffer
[
j
];
buffer
[
j
]
=
settings
.
placeholder
;
}
else
{
break
;
}
j
=
seekNext
(
j
);
}
}
writeBuffer
();
input
.
caret
(
Math
.
max
(
firstNonMaskPos
,
begin
));
}
function
shiftR
(
pos
)
{
var
i
,
c
,
j
,
t
;
for
(
i
=
pos
,
c
=
settings
.
placeholder
;
i
<
len
;
i
++
)
{
if
(
tests
[
i
])
{
j
=
seekNext
(
i
);
t
=
buffer
[
i
];
buffer
[
i
]
=
c
;
if
(
j
<
len
&&
tests
[
j
].
test
(
t
))
{
c
=
t
;
}
else
{
break
;
}
}
}
}
function
keydownEvent
(
e
)
{
var
k
=
e
.
which
,
pos
,
begin
,
end
;
//backspace, delete, and escape get special treatment
if
(
k
===
8
||
k
===
46
||
(
iPhone
&&
k
===
127
))
{
pos
=
input
.
caret
();
begin
=
pos
.
begin
;
end
=
pos
.
end
;
if
(
end
-
begin
===
0
)
{
begin
=
k
!==
46
?
seekPrev
(
begin
):(
end
=
seekNext
(
begin
-
1
));
end
=
k
===
46
?
seekNext
(
end
):
end
;
}
clearBuffer
(
begin
,
end
);
shiftL
(
begin
,
end
-
1
);
e
.
preventDefault
();
}
else
if
(
k
==
27
)
{
//escape
input
.
val
(
focusText
);
input
.
caret
(
0
,
checkVal
());
e
.
preventDefault
();
}
}
function
keypressEvent
(
e
)
{
var
k
=
e
.
which
,
pos
=
input
.
caret
(),
p
,
c
,
next
;
if
(
e
.
ctrlKey
||
e
.
altKey
||
e
.
metaKey
||
k
<
32
)
{
//Ignore
return
;
}
else
if
(
k
)
{
if
(
pos
.
end
-
pos
.
begin
!==
0
){
clearBuffer
(
pos
.
begin
,
pos
.
end
);
shiftL
(
pos
.
begin
,
pos
.
end
-
1
);
}
p
=
seekNext
(
pos
.
begin
-
1
);
if
(
p
<
len
)
{
c
=
String
.
fromCharCode
(
k
);
if
(
tests
[
p
].
test
(
c
))
{
shiftR
(
p
);
buffer
[
p
]
=
c
;
writeBuffer
();
next
=
seekNext
(
p
);
if
(
android
){
setTimeout
(
$
.
proxy
(
$
.
fn
.
caret
,
input
,
next
),
0
);
}
else
{
input
.
caret
(
next
);
}
if
(
settings
.
completed
&&
next
>=
len
)
{
settings
.
completed
.
call
(
input
);
}
}
}
e
.
preventDefault
();
}
}
function
clearBuffer
(
start
,
end
)
{
var
i
;
for
(
i
=
start
;
i
<
end
&&
i
<
len
;
i
++
)
{
if
(
tests
[
i
])
{
buffer
[
i
]
=
settings
.
placeholder
;
}
}
}
function
writeBuffer
()
{
input
.
val
(
buffer
.
join
(
''
));
}
function
checkVal
(
allow
)
{
//try to place characters where they belong
var
test
=
input
.
val
(),
lastMatch
=
-
1
,
i
,
c
;
for
(
i
=
0
,
pos
=
0
;
i
<
len
;
i
++
)
{
if
(
tests
[
i
])
{
buffer
[
i
]
=
settings
.
placeholder
;
while
(
pos
++
<
test
.
length
)
{
c
=
test
.
charAt
(
pos
-
1
);
if
(
tests
[
i
].
test
(
c
))
{
buffer
[
i
]
=
c
;
lastMatch
=
i
;
break
;
}
}
if
(
pos
>
test
.
length
)
{
break
;
}
}
else
if
(
buffer
[
i
]
===
test
.
charAt
(
pos
)
&&
i
!==
partialPosition
)
{
pos
++
;
lastMatch
=
i
;
}
}
if
(
allow
)
{
writeBuffer
();
}
else
if
(
lastMatch
+
1
<
partialPosition
)
{
input
.
val
(
""
);
clearBuffer
(
0
,
len
);
}
else
{
writeBuffer
();
input
.
val
(
input
.
val
().
substring
(
0
,
lastMatch
+
1
));
}
return
(
partialPosition
?
i
:
firstNonMaskPos
);
}
input
.
data
(
$
.
mask
.
dataName
,
function
(){
return
$
.
map
(
buffer
,
function
(
c
,
i
)
{
return
tests
[
i
]
&&
c
!=
settings
.
placeholder
?
c
:
null
;
}).
join
(
''
);
});
if
(
!
input
.
attr
(
"readonly"
))
input
.
one
(
"unmask"
,
function
()
{
input
.
unbind
(
".mask"
)
.
removeData
(
$
.
mask
.
dataName
);
})
.
bind
(
"focus.mask"
,
function
()
{
clearTimeout
(
caretTimeoutId
);
var
pos
,
moveCaret
;
focusText
=
input
.
val
();
pos
=
checkVal
();
caretTimeoutId
=
setTimeout
(
function
(){
writeBuffer
();
if
(
pos
==
mask
.
length
)
{
input
.
caret
(
0
,
pos
);
}
else
{
input
.
caret
(
pos
);
}
},
10
);
})
.
bind
(
"blur.mask"
,
function
()
{
checkVal
();
if
(
input
.
val
()
!=
focusText
)
input
.
change
();
})
.
bind
(
"keydown.mask"
,
keydownEvent
)
.
bind
(
"keypress.mask"
,
keypressEvent
)
.
bind
(
pasteEventName
,
function
()
{
setTimeout
(
function
()
{
var
pos
=
checkVal
(
true
);
input
.
caret
(
pos
);
if
(
settings
.
completed
&&
pos
==
input
.
val
().
length
)
settings
.
completed
.
call
(
input
);
},
0
);
});
checkVal
();
//Perform initial check for existing values
});
}
});
})(
jQuery
);
framework/widgets/MaskedInput.php
View file @
d6d48de9
...
...
@@ -11,13 +11,13 @@ use yii\base\InvalidConfigException;
use
yii\helpers\Html
;
use
yii\helpers\Json
;
use
yii\web\JsExpression
;
use
yii\web\View
;
/**
* MaskedInput generates a masked text input.
*
* MaskedInput is similar to [[Html::textInput()]] except that
* an input mask will be used to force users to enter properly formatted data,
* such as phone numbers, social security numbers.
* MaskedInput is similar to [[Html::textInput()]] except that an input mask will be used to force users to enter
* properly formatted data, such as phone numbers, social security numbers.
*
* To use MaskedInput, you must set the [[mask]] property. The following example
* shows how to use MaskedInput to collect phone numbers:
...
...
@@ -29,38 +29,54 @@ use yii\web\JsExpression;
* ]);
* ~~~
*
* The masked text field is implemented based on the [jQuery masked input plugin](http://digitalbush.com/projects/masked-input-plugin).
* The masked text field is implemented based on the
* [jQuery input mask plugin](https://github.com/RobinHerbots/jquery.inputmask).
*
* @author
Qiang Xue <qiang.xue
@gmail.com>
* @author
Kartik Visweswaran <kartikv2
@gmail.com>
* @since 2.0
*/
class
MaskedInput
extends
InputWidget
{
const
PLUGIN_NAME
=
'inputmask'
;
/**
* @var string the input mask (e.g. '99/99/9999' for date input). The following characters are predefined:
* @var string|array|JsExpression the input mask (e.g. '99/99/9999' for date input). The following characters
* can be used in the mask and are predefined:
*
* - `a`: represents an alpha character (A-Z, a-z)
* - `9`: represents a numeric character (0-9)
* - `*`: represents an alphanumeric character (A-Z, a-z, 0-9)
* - `?`: anything listed after '?' within the mask is considered optional user input
* - `[` and `]`: anything entered between the square brackets is considered optional user input. This is
* based on the `optionalmarker` setting in `clientOptions`.
*
* Additional
characters can be defined by specifying the [[charMap]]
property.
* Additional
definitions can be set through definitions
property.
*/
public
$mask
;
/**
* @var array the mapping between mask characters and the corresponding patterns.
* For example, `['~' => '[+-]']` specifies that the '~' character expects '+' or '-' input.
* Defaults to null, meaning using the map as described in [[mask]].
* @var array custom mask definitions to use. Should be configured as `$maskSymbol => $settings`, where:
* - `maskSymbol`: string, a character to identify your mask definition
* - `settings`: array, consisiting of the following entries:
* - `validator`: string, a JS regular expression or a JS function.
* - `cardinality`: int, specifies how many characters are represented and validated for the definition.
* - `prevalidator`: array, validate the characters before the definition cardinality is reached.
* - `definitionSymbol`: string, allows shifting values from other definitions, with this `definitionSymbol`.
*/
public
$charMap
;
public
$definitions
;
/**
* @var string the character prompting for user input. Defaults to underscore '_'.
* @var array custom aliases to use. Should be configured as `$maskAlias` => $settings`, where:
* - `maskAlias`: string, a text to identify your mask alias definition (e.g. 'phone')
* - `settings`: array settings for the mask symbol, exactly similar to parameters as passed in `clientOptions`
*/
public
$placeholder
;
public
$aliases
;
/**
* @var string a JavaScript function callback that will be invoked when user finishes the input.
* @var array the JQuery plugin options for the input mask plugin.
* @see https://github.com/RobinHerbots/jquery.inputmask
*/
public
$completed
;
public
$clientOptions
=
[];
/**
* @var array the HTML attributes for the input tag.
* @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered.
...
...
@@ -68,19 +84,28 @@ class MaskedInput extends InputWidget
public
$options
=
[
'class'
=>
'form-control'
];
/**
* @var string the hashed variable to store the pluginOptions
*/
protected
$_hashVar
;
/**
* Initializes the widget.
*
* @throws InvalidConfigException if the "mask" property is not set.
*/
public
function
init
()
{
parent
::
init
();
if
(
empty
(
$this
->
mask
))
{
throw
new
InvalidConfigException
(
'The "mask" property must be set.'
);
if
(
empty
(
$this
->
mask
)
&&
empty
(
$this
->
clientOptions
[
'alias'
])
)
{
throw
new
InvalidConfigException
(
"Either the 'mask' property or the 'clientOptions[
\"
alias
\"
]' property must be set."
);
}
}
/**
*
* Runs the widget.
*
* @return string|void
*/
public
function
run
()
{
...
...
@@ -93,41 +118,60 @@ class MaskedInput extends InputWidget
}
/**
* Registers the needed JavaScript.
* Generates a hashed variable to store the plugin `clientOptions`. Helps in reusing the variable for similar
* options passed for other widgets on the same page. The following special data attributes will also be
* setup for the input widget, that can be accessed through javascript:
* - 'data-plugin-options' will store the hashed variable storing the plugin options.
* - 'data-plugin-name' the name of the plugin
*
* @param View $view the view instance
*/
p
ublic
function
registerClientScript
(
)
p
rotected
function
hashPluginOptions
(
$view
)
{
$options
=
$this
->
getClientOptions
();
$options
=
empty
(
$options
)
?
''
:
','
.
Json
::
encode
(
$options
);
$js
=
''
;
if
(
is_array
(
$this
->
charMap
)
&&
!
empty
(
$this
->
charMap
))
{
$js
.=
'jQuery.mask.definitions='
.
Json
::
encode
(
$this
->
charMap
)
.
";
\n
"
;
}
$id
=
$this
->
options
[
'id'
];
$js
.=
"jQuery(
\"
#
{
$id
}
\"
).mask(
\"
{
$this
->
mask
}
\"
{
$options
}
);"
;
$view
=
$this
->
getView
();
MaskedInputAsset
::
register
(
$view
);
$view
->
registerJs
(
$js
);
$encOptions
=
empty
(
$this
->
clientOptions
)
?
'{}'
:
Json
::
encode
(
$this
->
clientOptions
);
$this
->
_hashVar
=
self
::
PLUGIN_NAME
.
'_'
.
hash
(
'crc32'
,
$encOptions
);
$this
->
options
[
'data-plugin-name'
]
=
self
::
PLUGIN_NAME
;
$this
->
options
[
'data-plugin-options'
]
=
$this
->
_hashVar
;
$view
->
registerJs
(
"var
{
$this
->
_hashVar
}
=
{
$encOptions
}
;
\n
"
,
View
::
POS_HEAD
);
}
/**
*
@return array the options for the text field
*
Initializes client options
*/
protected
function
ge
tClientOptions
()
protected
function
ini
tClientOptions
()
{
$options
=
[];
if
(
$this
->
placeholder
!==
null
)
{
$options
[
'placeholder'
]
=
$this
->
placeholder
;
$options
=
$this
->
clientOptions
;
foreach
(
$options
as
$key
=>
$value
)
{
if
(
in_array
(
$key
,
[
'oncomplete'
,
'onincomplete'
,
'oncleared'
,
'onKeyUp'
,
'onKeyDown'
,
'onBeforeMask'
,
'onBeforePaste'
,
'onUnMask'
,
'isComplete'
,
'determineActiveMasksetIndex'
])
&&
!
$value
instanceof
JsExpression
)
{
$options
[
$key
]
=
new
JsExpression
(
$value
);
}
if
(
$this
->
completed
!==
null
)
{
if
(
$this
->
completed
instanceof
JsExpression
)
{
$options
[
'completed'
]
=
$this
->
completed
;
}
else
{
$options
[
'completed'
]
=
new
JsExpression
(
$this
->
completed
);
}
$this
->
clientOptions
=
$options
;
}
return
$options
;
/**
* Registers the needed client script and options.
*/
public
function
registerClientScript
()
{
$js
=
''
;
$view
=
$this
->
getView
();
$this
->
initClientOptions
();
if
(
!
empty
(
$this
->
mask
))
{
$this
->
clientOptions
[
'mask'
]
=
$this
->
mask
;
}
$this
->
hashPluginOptions
(
$view
);
if
(
is_array
(
$this
->
definitions
)
&&
!
empty
(
$this
->
definitions
))
{
$js
.=
'$.extend($.'
.
self
::
PLUGIN_NAME
.
'.defaults.definitions, '
.
Json
::
encode
(
$this
->
definitions
)
.
");
\n
"
;
}
if
(
is_array
(
$this
->
aliases
)
&&
!
empty
(
$this
->
aliases
))
{
$js
.=
'$.extend($.'
.
self
::
PLUGIN_NAME
.
'.defaults.aliases, '
.
Json
::
encode
(
$this
->
aliases
)
.
");
\n
"
;
}
$id
=
$this
->
options
[
'id'
];
$js
.=
'$("#'
.
$id
.
'").'
.
self
::
PLUGIN_NAME
.
"("
.
$this
->
_hashVar
.
");
\n
"
;
MaskedInputAsset
::
register
(
$view
);
$view
->
registerJs
(
$js
);
}
}
framework/widgets/MaskedInputAsset.php
View file @
d6d48de9
...
...
@@ -10,16 +10,15 @@ namespace yii\widgets;
use
yii\web\AssetBundle
;
/**
* @author Qiang Xue <qiang.xue@gmail.com>
* The asset bundle for the [[MaskedInput]] widget.
* Includes client assets of [jQuery input mask plugin](https://github.com/RobinHerbots/jquery.inputmask).
*
* @author Kartik Visweswaran <kartikv2@gmail.com>
* @since 2.0
*/
class
MaskedInputAsset
extends
AssetBundle
{
public
$sourcePath
=
'@yii/assets'
;
public
$js
=
[
'jquery.maskedinput.js'
,
];
public
$depends
=
[
'yii\web\YiiAsset'
,
];
public
$js
=
[
'jquery.inputmask.bundle.min.js'
];
public
$depends
=
[
'yii\web\YiiAsset'
];
}
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